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:
+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);
|
||||
}
|
||||
Reference in New Issue
Block a user