mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-09-22 17:16:19 -07:00
feat(dictionary): add Hachidori backend support
- Add backend selection, setup gating, Anki integration, and external host support - Add launcher flags, documentation, packaging, and focused tests - Open on-demand overlay modals on the first attempt
This commit is contained in:
Vendored
+102
@@ -0,0 +1,102 @@
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
project(hachidori LANGUAGES C CXX)
|
||||
|
||||
option(HACHIDORI_PTHREADS "Build a pthread runtime" ON)
|
||||
# With pthreads, WasmFS + direct OPFS is the primary runtime. Turning this off
|
||||
# keeps the threads but stores through the classic FS + IDBFS, for hosts such as
|
||||
# Electron that expose shared memory and workers but refuse OPFS access handles
|
||||
# to chrome-extension:// origins.
|
||||
option(HACHIDORI_WASMFS "Use WasmFS + direct OPFS with pthreads" ON)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# -fwasm-exceptions is an ABI choice: every translation unit that appears in the
|
||||
# final link must agree, including the engine's and its vendored C deps. Putting
|
||||
# it in CMAKE_{C,CXX}_FLAGS rather than on our target is what makes that true for
|
||||
# the add_subdirectory'd trees.
|
||||
# Every supported host (Chrome >= 128, Electron, Node) has wasm SIMD, and
|
||||
# link-time optimisation lets the engine, zstd, glaze, and libdeflate inline
|
||||
# across each other (measured: native lookups 15-30% faster, imports 4-8%;
|
||||
# output files identical). xxHash would route wasm SIMD through the SIMDe
|
||||
# arm_neon.h shim, which drags C++ headers inside its extern "C" block; keep
|
||||
# xxHash scalar and let the compiler vectorise everything else.
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fwasm-exceptions -O3 -msimd128 -flto")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fwasm-exceptions -O3 -msimd128 -flto")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D'XXH_HAS_INCLUDE(x)=0'")
|
||||
if(HACHIDORI_PTHREADS)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||
endif()
|
||||
|
||||
# The WasmFS runtime reads dictionary files through sync access handles (see
|
||||
# hoshidicts src/memory/memory.cpp); the classic-FS runtimes must not.
|
||||
if(HACHIDORI_PTHREADS AND HACHIDORI_WASMFS)
|
||||
set(HOSHIDICTS_WASMFS ON CACHE BOOL "" FORCE)
|
||||
else()
|
||||
set(HOSHIDICTS_WASMFS OFF CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../third_party/hoshidicts hoshidicts)
|
||||
|
||||
add_executable(hoshidicts_wasm bindings.cpp)
|
||||
|
||||
if(HACHIDORI_PTHREADS AND NOT HACHIDORI_WASMFS)
|
||||
set_target_properties(hoshidicts_wasm PROPERTIES OUTPUT_NAME hoshidicts-threaded-idbfs SUFFIX ".mjs")
|
||||
elseif(HACHIDORI_PTHREADS)
|
||||
set_target_properties(hoshidicts_wasm PROPERTIES OUTPUT_NAME hoshidicts-threaded SUFFIX ".mjs")
|
||||
else()
|
||||
set_target_properties(hoshidicts_wasm PROPERTIES OUTPUT_NAME hoshidicts SUFFIX ".mjs")
|
||||
endif()
|
||||
|
||||
# The engine links glaze PRIVATE, so its include dirs do not propagate here.
|
||||
target_link_libraries(hoshidicts_wasm PRIVATE hoshidicts glaze::glaze)
|
||||
|
||||
# src/ is not in the engine's public include set. hdw_import reaches into it for
|
||||
# zip/zip.hpp so it can read the archive's declared title before handing the
|
||||
# archive to an importer that turns that title into a directory it may delete.
|
||||
target_include_directories(hoshidicts_wasm PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../third_party/hoshidicts/src)
|
||||
|
||||
if(HACHIDORI_PTHREADS AND HACHIDORI_WASMFS)
|
||||
target_compile_definitions(hoshidicts_wasm PRIVATE HACHIDORI_OPFS=1)
|
||||
endif()
|
||||
|
||||
target_link_options(hoshidicts_wasm PRIVATE
|
||||
-O3
|
||||
-fwasm-exceptions
|
||||
-flto
|
||||
-sMODULARIZE=1
|
||||
-sEXPORT_ES6=1
|
||||
-sEXPORT_NAME=createHoshidicts
|
||||
-sENVIRONMENT=web,worker,node
|
||||
-sALLOW_MEMORY_GROWTH=1
|
||||
-sMAXIMUM_MEMORY=4GB
|
||||
-sSTACK_SIZE=8MB
|
||||
-sFORCE_FILESYSTEM=1
|
||||
-sEXPORTED_FUNCTIONS=_malloc,_free,_hdw_init_storage,_hdw_import,_hdw_reset,_hdw_add_dict,_hdw_remove_dict,_hdw_set_dict_order,_hdw_frequencies,_hdw_lookup,_hdw_lookup_dictionary,_hdw_kanji,_hdw_styles,_hdw_media,_hdw_media_data,_hdw_last_error
|
||||
)
|
||||
|
||||
if(HACHIDORI_PTHREADS)
|
||||
target_link_options(hoshidicts_wasm PRIVATE
|
||||
-pthread
|
||||
"SHELL:-sPTHREAD_POOL_SIZE=Math.max(2,Math.min(globalThis.navigator?.hardwareConcurrency||1,8))+1"
|
||||
-sPTHREAD_POOL_SIZE_STRICT=2
|
||||
-sPTHREAD_POOL_DELAY_LOAD=0
|
||||
-sALLOW_BLOCKING_ON_MAIN_THREAD=0
|
||||
)
|
||||
endif()
|
||||
|
||||
if(HACHIDORI_PTHREADS AND HACHIDORI_WASMFS)
|
||||
target_link_options(hoshidicts_wasm PRIVATE
|
||||
-sWASMFS=1
|
||||
-sEXPORTED_RUNTIME_METHODS=ccall,cwrap,FS,UTF8ToString,stringToNewUTF8,HEAPU8,lengthBytesUTF8
|
||||
)
|
||||
else()
|
||||
target_link_options(hoshidicts_wasm PRIVATE
|
||||
-lidbfs.js
|
||||
-sEXPORTED_RUNTIME_METHODS=ccall,cwrap,FS,IDBFS,UTF8ToString,stringToNewUTF8,HEAPU8,lengthBytesUTF8
|
||||
)
|
||||
endif()
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
cmake_minimum_required(VERSION 3.31)
|
||||
|
||||
project(hachidori_avif LANGUAGES C)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
|
||||
set(AVIF_CODEC_AOM LOCAL CACHE STRING "" FORCE)
|
||||
set(AVIF_CODEC_AOM_DECODE OFF CACHE BOOL "" FORCE)
|
||||
set(AVIF_CODEC_AOM_ENCODE ON CACHE BOOL "" FORCE)
|
||||
set(AVIF_LIBYUV OFF CACHE BOOL "" FORCE)
|
||||
set(AVIF_LIBSHARPYUV OFF CACHE BOOL "" FORCE)
|
||||
set(AVIF_JPEG OFF CACHE BOOL "" FORCE)
|
||||
set(AVIF_ZLIBPNG OFF CACHE BOOL "" FORCE)
|
||||
set(AVIF_BUILD_APPS OFF CACHE BOOL "" FORCE)
|
||||
set(AVIF_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(AOM_TARGET_CPU generic CACHE STRING "" FORCE)
|
||||
set(CONFIG_RUNTIME_CPU_DETECT 0 CACHE STRING "" FORCE)
|
||||
|
||||
FetchContent_Declare(
|
||||
libavif
|
||||
GIT_REPOSITORY https://github.com/AOMediaCodec/libavif.git
|
||||
GIT_TAG 1aadfad932c98c069a1204261b1856f81f3bc199
|
||||
GIT_SHALLOW FALSE
|
||||
)
|
||||
FetchContent_MakeAvailable(libavif)
|
||||
|
||||
add_executable(hachidori_avif encoder.c)
|
||||
set_target_properties(hachidori_avif PROPERTIES OUTPUT_NAME avif-encoder SUFFIX ".mjs")
|
||||
target_compile_features(hachidori_avif PRIVATE c_std_11)
|
||||
target_compile_options(hachidori_avif PRIVATE -Oz)
|
||||
target_link_libraries(hachidori_avif PRIVATE avif)
|
||||
target_link_options(hachidori_avif PRIVATE
|
||||
-Oz
|
||||
-sMODULARIZE=1
|
||||
-sEXPORT_ES6=1
|
||||
-sEXPORT_NAME=createAvifEncoderModule
|
||||
-sENVIRONMENT=worker,node
|
||||
-sALLOW_MEMORY_GROWTH=1
|
||||
-sINITIAL_MEMORY=32MB
|
||||
-sMAXIMUM_MEMORY=256MB
|
||||
-sSTACK_SIZE=1MB
|
||||
-sFILESYSTEM=0
|
||||
-sMALLOC=emmalloc
|
||||
-sEXPORTED_FUNCTIONS=_malloc,_free,_hda_create,_hda_add_rgba,_hda_finish,_hda_output,_hda_output_size,_hda_last_error,_hda_destroy
|
||||
-sEXPORTED_RUNTIME_METHODS=HEAPU8,UTF8ToString
|
||||
)
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
The AVIF encoder binary links libavif v1.3.0 and libaom v3.12.1. It also
|
||||
includes libavif's bundled libyuv conversion sources.
|
||||
|
||||
libavif
|
||||
Copyright 2019 Joe Drago. 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.
|
||||
|
||||
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.
|
||||
|
||||
libaom
|
||||
Copyright (c) 2016, Alliance for Open Media. 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.
|
||||
|
||||
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.
|
||||
|
||||
libyuv
|
||||
Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* 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.
|
||||
|
||||
* Neither the name of Google 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.
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
# Build the pinned libavif/libaom sequence encoder used by the capture worker.
|
||||
set -e
|
||||
|
||||
AVIF_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
WASM_DIR=$(CDPATH= cd -- "$AVIF_DIR/.." && pwd)
|
||||
REPO_ROOT=$(CDPATH= cd -- "$WASM_DIR/.." && pwd)
|
||||
AVIF_BUILD_DIR="${AVIF_BUILD_DIR:-$AVIF_DIR/build}"
|
||||
VENDOR_DIR="$REPO_ROOT/extension/vendor"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$WASM_DIR/env.sh"
|
||||
|
||||
emcmake cmake -S "$AVIF_DIR" -B "$AVIF_BUILD_DIR" -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel
|
||||
cmake --build "$AVIF_BUILD_DIR" --parallel "$(nproc 2>/dev/null || printf '%s\n' 4)"
|
||||
|
||||
mkdir -p "$VENDOR_DIR"
|
||||
cp "$AVIF_BUILD_DIR/avif-encoder.mjs" "$VENDOR_DIR/avif-encoder.mjs"
|
||||
cp "$AVIF_BUILD_DIR/avif-encoder.wasm" "$VENDOR_DIR/avif-encoder.wasm"
|
||||
chmod 644 "$VENDOR_DIR/avif-encoder.mjs" "$VENDOR_DIR/avif-encoder.wasm"
|
||||
ls -l "$VENDOR_DIR/avif-encoder.mjs" "$VENDOR_DIR/avif-encoder.wasm"
|
||||
Vendored
+149
@@ -0,0 +1,149 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
//
|
||||
// Small C ABI around libavif's sequence encoder. The caller supplies one RGBA
|
||||
// frame at a time, so decoded frame memory never accumulates in WebAssembly.
|
||||
|
||||
#include <emscripten/emscripten.h>
|
||||
#include <avif/avif.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
enum {
|
||||
HDA_MAX_WIDTH = 640,
|
||||
HDA_MAX_HEIGHT = 360,
|
||||
HDA_MAX_FRAMES = 120,
|
||||
HDA_MAX_OUTPUT_BYTES = 4 * 1024 * 1024
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
avifEncoder *encoder;
|
||||
avifRWData output;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint32_t frame_count;
|
||||
int finished;
|
||||
char error[256];
|
||||
} HdaEncoder;
|
||||
|
||||
static char global_error[256];
|
||||
|
||||
static int fail(HdaEncoder *encoder, const char *message) {
|
||||
char *target = encoder ? encoder->error : global_error;
|
||||
snprintf(target, 256, "%s", message ? message : "unknown AVIF encoder failure");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int avif_fail(HdaEncoder *encoder, const char *operation, avifResult result) {
|
||||
char message[256];
|
||||
snprintf(message, sizeof(message), "%s: %s", operation, avifResultToString(result));
|
||||
return fail(encoder, message);
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
HdaEncoder *hda_create(uint32_t width, uint32_t height, uint32_t timescale, int quality, int speed) {
|
||||
global_error[0] = '\0';
|
||||
if (width == 0 || height == 0 || width > HDA_MAX_WIDTH || height > HDA_MAX_HEIGHT
|
||||
|| (width & 1U) != 0 || (height & 1U) != 0) {
|
||||
fail(NULL, "AVIF dimensions must be even and no larger than 640 by 360");
|
||||
return NULL;
|
||||
}
|
||||
if (timescale == 0 || quality < AVIF_QUALITY_WORST || quality > AVIF_QUALITY_BEST
|
||||
|| speed < AVIF_SPEED_SLOWEST || speed > AVIF_SPEED_FASTEST) {
|
||||
fail(NULL, "AVIF encoder settings are invalid");
|
||||
return NULL;
|
||||
}
|
||||
HdaEncoder *state = calloc(1, sizeof(HdaEncoder));
|
||||
if (!state) {
|
||||
fail(NULL, "could not allocate AVIF encoder state");
|
||||
return NULL;
|
||||
}
|
||||
state->encoder = avifEncoderCreate();
|
||||
if (!state->encoder) {
|
||||
free(state);
|
||||
fail(NULL, "could not create AVIF encoder");
|
||||
return NULL;
|
||||
}
|
||||
state->width = width;
|
||||
state->height = height;
|
||||
state->encoder->codecChoice = AVIF_CODEC_CHOICE_AOM;
|
||||
state->encoder->maxThreads = 1;
|
||||
state->encoder->speed = speed;
|
||||
state->encoder->quality = quality;
|
||||
state->encoder->qualityAlpha = quality;
|
||||
state->encoder->timescale = timescale;
|
||||
state->encoder->repetitionCount = AVIF_REPETITION_COUNT_INFINITE;
|
||||
state->encoder->keyframeInterval = 0;
|
||||
return state;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int hda_add_rgba(HdaEncoder *state, const uint8_t *rgba, size_t byte_length, uint32_t duration) {
|
||||
if (!state || !state->encoder || state->finished) return fail(state, "AVIF encoder is not writable");
|
||||
if (!rgba || byte_length != (size_t)state->width * state->height * 4U) {
|
||||
return fail(state, "RGBA frame size does not match the AVIF canvas");
|
||||
}
|
||||
if (duration == 0 || state->frame_count >= HDA_MAX_FRAMES) {
|
||||
return fail(state, "AVIF frame count or duration exceeds its limit");
|
||||
}
|
||||
avifImage *image = avifImageCreate(state->width, state->height, 8, AVIF_PIXEL_FORMAT_YUV420);
|
||||
if (!image) return fail(state, "could not allocate AVIF image");
|
||||
image->yuvRange = AVIF_RANGE_FULL;
|
||||
image->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709;
|
||||
image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB;
|
||||
image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT709;
|
||||
|
||||
avifRGBImage rgb;
|
||||
avifRGBImageSetDefaults(&rgb, image);
|
||||
rgb.format = AVIF_RGB_FORMAT_RGBA;
|
||||
rgb.ignoreAlpha = AVIF_TRUE;
|
||||
rgb.pixels = (uint8_t *)rgba;
|
||||
rgb.rowBytes = state->width * 4U;
|
||||
avifResult result = avifImageRGBToYUV(image, &rgb);
|
||||
if (result == AVIF_RESULT_OK) {
|
||||
result = avifEncoderAddImage(state->encoder, image, duration, AVIF_ADD_IMAGE_FLAG_NONE);
|
||||
}
|
||||
avifImageDestroy(image);
|
||||
if (result != AVIF_RESULT_OK) return avif_fail(state, "could not add AVIF frame", result);
|
||||
state->frame_count += 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
int hda_finish(HdaEncoder *state) {
|
||||
if (!state || !state->encoder || state->finished || state->frame_count == 0) {
|
||||
return fail(state, "AVIF encoder has no finishable sequence");
|
||||
}
|
||||
avifResult result = avifEncoderFinish(state->encoder, &state->output);
|
||||
if (result != AVIF_RESULT_OK) return avif_fail(state, "could not finish AVIF sequence", result);
|
||||
if (state->output.size == 0 || state->output.size > HDA_MAX_OUTPUT_BYTES) {
|
||||
avifRWDataFree(&state->output);
|
||||
return fail(state, "Animated AVIF exceeds the 4 MiB output limit");
|
||||
}
|
||||
state->finished = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
const uint8_t *hda_output(const HdaEncoder *state) {
|
||||
return state && state->finished ? state->output.data : NULL;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
size_t hda_output_size(const HdaEncoder *state) {
|
||||
return state && state->finished ? state->output.size : 0;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
const char *hda_last_error(const HdaEncoder *state) {
|
||||
return state ? state->error : global_error;
|
||||
}
|
||||
|
||||
EMSCRIPTEN_KEEPALIVE
|
||||
void hda_destroy(HdaEncoder *state) {
|
||||
if (!state) return;
|
||||
avifRWDataFree(&state->output);
|
||||
avifEncoderDestroy(state->encoder);
|
||||
free(state);
|
||||
}
|
||||
Vendored
+1231
File diff suppressed because it is too large
Load Diff
+52
@@ -0,0 +1,52 @@
|
||||
#!/bin/sh
|
||||
# Builds the threaded OPFS runtime, the threaded IDBFS runtime for hosts without
|
||||
# OPFS access handles, and the single-thread IDBFS fallback.
|
||||
set -e
|
||||
|
||||
WASM_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
REPO_ROOT=$(CDPATH= cd -- "$WASM_DIR/.." && pwd)
|
||||
THREADED_BUILD_DIR="${BUILD_DIR:-$WASM_DIR/build}"
|
||||
THREADED_IDBFS_BUILD_DIR="${THREADED_IDBFS_BUILD_DIR:-${THREADED_BUILD_DIR}-idbfs}"
|
||||
FALLBACK_BUILD_DIR="${FALLBACK_BUILD_DIR:-${THREADED_BUILD_DIR}-fallback}"
|
||||
VENDOR_DIR="$REPO_ROOT/extension/vendor"
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
. "$WASM_DIR/env.sh"
|
||||
|
||||
build_runtime() {
|
||||
build_dir=$1
|
||||
pthreads=$2
|
||||
wasmfs=$3
|
||||
emcmake cmake -S "$WASM_DIR" -B "$build_dir" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DHACHIDORI_PTHREADS="$pthreads" \
|
||||
-DHACHIDORI_WASMFS="$wasmfs"
|
||||
cmake --build "$build_dir" --parallel "$(nproc 2>/dev/null || printf '%s\n' 4)"
|
||||
}
|
||||
|
||||
build_runtime "$THREADED_BUILD_DIR" ON ON
|
||||
build_runtime "$THREADED_IDBFS_BUILD_DIR" ON OFF
|
||||
build_runtime "$FALLBACK_BUILD_DIR" OFF ON
|
||||
|
||||
mkdir -p "$VENDOR_DIR"
|
||||
cp "$THREADED_BUILD_DIR/hoshidicts-threaded.mjs" "$VENDOR_DIR/hoshidicts-threaded.mjs"
|
||||
cp "$THREADED_BUILD_DIR/hoshidicts-threaded.wasm" "$VENDOR_DIR/hoshidicts-threaded.wasm"
|
||||
cp "$THREADED_IDBFS_BUILD_DIR/hoshidicts-threaded-idbfs.mjs" "$VENDOR_DIR/hoshidicts-threaded-idbfs.mjs"
|
||||
cp "$THREADED_IDBFS_BUILD_DIR/hoshidicts-threaded-idbfs.wasm" "$VENDOR_DIR/hoshidicts-threaded-idbfs.wasm"
|
||||
cp "$FALLBACK_BUILD_DIR/hoshidicts.mjs" "$VENDOR_DIR/hoshidicts.mjs"
|
||||
cp "$FALLBACK_BUILD_DIR/hoshidicts.wasm" "$VENDOR_DIR/hoshidicts.wasm"
|
||||
chmod 644 \
|
||||
"$VENDOR_DIR/hoshidicts-threaded.mjs" \
|
||||
"$VENDOR_DIR/hoshidicts-threaded.wasm" \
|
||||
"$VENDOR_DIR/hoshidicts-threaded-idbfs.mjs" \
|
||||
"$VENDOR_DIR/hoshidicts-threaded-idbfs.wasm" \
|
||||
"$VENDOR_DIR/hoshidicts.mjs" \
|
||||
"$VENDOR_DIR/hoshidicts.wasm"
|
||||
|
||||
ls -l \
|
||||
"$VENDOR_DIR/hoshidicts-threaded.mjs" \
|
||||
"$VENDOR_DIR/hoshidicts-threaded.wasm" \
|
||||
"$VENDOR_DIR/hoshidicts-threaded-idbfs.mjs" \
|
||||
"$VENDOR_DIR/hoshidicts-threaded-idbfs.wasm" \
|
||||
"$VENDOR_DIR/hoshidicts.mjs" \
|
||||
"$VENDOR_DIR/hoshidicts.wasm"
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/sh
|
||||
# Shared Emscripten environment for every build entry point in this repo.
|
||||
#
|
||||
# emsdk's launchers run `python3` from PATH and require >= 3.10, but this host's
|
||||
# /usr/bin/python3 is 3.9. EMSHIM puts a newer python3 first on PATH.
|
||||
EMSDK_DIR="${EMSDK_DIR:-$HOME/emsdk}"
|
||||
EMSHIM="${EMSHIM:-$HOME/.local/emshim}"
|
||||
|
||||
if [ ! -x "$EMSHIM/python3" ]; then
|
||||
mkdir -p "$EMSHIM"
|
||||
for v in 3.13 3.12 3.11 3.10; do
|
||||
if [ -x "$HOME/.local/bin/python$v" ]; then
|
||||
ln -sf "$HOME/.local/bin/python$v" "$EMSHIM/python3"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
PATH="$EMSHIM:$PATH"
|
||||
export PATH
|
||||
export EMSDK_PYTHON="$EMSHIM/python3"
|
||||
# shellcheck disable=SC1091
|
||||
. "$EMSDK_DIR/emsdk_env.sh" >/dev/null 2>&1
|
||||
PATH="$EMSHIM:$PATH"
|
||||
export PATH
|
||||
Reference in New Issue
Block a user