Switch code to SDL2.

Also simplify wasm html (now that it is suppported by all browsers).

Change-Id: I352b08594b93d3dd7d44832d4328b3546ccc1b90
This commit is contained in:
Vincent Rabaud 2023-09-28 10:18:23 +02:00
parent a429c0de64
commit 24d7f9cb6e
7 changed files with 83 additions and 120 deletions

View File

@ -638,13 +638,13 @@ if(WEBP_BUILD_EXTRAS)
${CMAKE_CURRENT_BINARY_DIR}) ${CMAKE_CURRENT_BINARY_DIR})
# vwebp_sdl # vwebp_sdl
find_package(SDL) find_package(SDL2 QUIET)
if(WEBP_BUILD_VWEBP AND SDL_FOUND) if(WEBP_BUILD_VWEBP AND SDL2_FOUND)
add_executable(vwebp_sdl ${VWEBP_SDL_SRCS}) add_executable(vwebp_sdl ${VWEBP_SDL_SRCS})
target_link_libraries(vwebp_sdl ${SDL_LIBRARY} imageioutil webp) target_link_libraries(vwebp_sdl ${SDL2_LIBRARIES} imageioutil webp)
target_include_directories( target_include_directories(
vwebp_sdl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} vwebp_sdl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_BINARY_DIR}/src ${SDL_INCLUDE_DIR}) ${CMAKE_CURRENT_BINARY_DIR}/src ${SDL2_INCLUDE_DIRS})
set(WEBP_HAVE_SDL 1) set(WEBP_HAVE_SDL 1)
target_compile_definitions(vwebp_sdl PUBLIC WEBP_HAVE_SDL) target_compile_definitions(vwebp_sdl PUBLIC WEBP_HAVE_SDL)
endif() endif()
@ -661,31 +661,43 @@ if(WEBP_BUILD_WEBP_JS)
else() else()
set(emscripten_stack_size "-sTOTAL_STACK=5MB") set(emscripten_stack_size "-sTOTAL_STACK=5MB")
endif() endif()
# Set SDL2 flags so that ports are downloaded by emscripten.
set(EMSCRIPTEN_SDL2_FLAGS "-sUSE_SDL=2 -sUSE_SDL_IMAGE=2")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EMSCRIPTEN_SDL2_FLAGS}")
# wasm2js does not support SIMD. # wasm2js does not support SIMD.
if(NOT WEBP_ENABLE_SIMD) if(NOT WEBP_ENABLE_SIMD)
# JavaScript version # JavaScript version
find_package(SDL2 QUIET)
add_executable(webp_js ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c) add_executable(webp_js ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c)
target_link_libraries(webp_js webpdecoder SDL) target_link_libraries(webp_js webpdecoder SDL2)
target_include_directories(webp_js PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(webp_js PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
set(WEBP_HAVE_SDL 1) set(WEBP_HAVE_SDL 1)
set_target_properties( set_target_properties(
webp_js webp_js
PROPERTIES LINK_FLAGS "-sWASM=0 ${emscripten_stack_size} \ PROPERTIES
LINK_FLAGS
"-sWASM=0 ${emscripten_stack_size} \
-sEXPORTED_FUNCTIONS=_WebPToSDL -sINVOKE_RUN=0 \ -sEXPORTED_FUNCTIONS=_WebPToSDL -sINVOKE_RUN=0 \
-sEXPORTED_RUNTIME_METHODS=cwrap") -sEXPORTED_RUNTIME_METHODS=cwrap ${EMSCRIPTEN_SDL2_FLAGS} \
-sALLOW_MEMORY_GROWTH"
)
set_target_properties(webp_js PROPERTIES OUTPUT_NAME webp) set_target_properties(webp_js PROPERTIES OUTPUT_NAME webp)
target_compile_definitions(webp_js PUBLIC EMSCRIPTEN WEBP_HAVE_SDL) target_compile_definitions(webp_js PUBLIC EMSCRIPTEN WEBP_HAVE_SDL)
endif() endif()
# WASM version # WASM version
add_executable(webp_wasm ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c) add_executable(webp_wasm ${CMAKE_CURRENT_SOURCE_DIR}/extras/webp_to_sdl.c)
target_link_libraries(webp_wasm webpdecoder SDL) target_link_libraries(webp_wasm webpdecoder SDL2)
target_include_directories(webp_wasm PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(webp_wasm PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
set_target_properties( set_target_properties(
webp_wasm webp_wasm
PROPERTIES LINK_FLAGS "-sWASM=1 ${emscripten_stack_size} \ PROPERTIES
LINK_FLAGS
"-sWASM=1 ${emscripten_stack_size} \
-sEXPORTED_FUNCTIONS=_WebPToSDL -sINVOKE_RUN=0 \ -sEXPORTED_FUNCTIONS=_WebPToSDL -sINVOKE_RUN=0 \
-sEXPORTED_RUNTIME_METHODS=cwrap") -sEXPORTED_RUNTIME_METHODS=cwrap ${EMSCRIPTEN_SDL2_FLAGS} \
-sALLOW_MEMORY_GROWTH"
)
target_compile_definitions(webp_wasm PUBLIC EMSCRIPTEN WEBP_HAVE_SDL) target_compile_definitions(webp_wasm PUBLIC EMSCRIPTEN WEBP_HAVE_SDL)
target_compile_definitions(webpdspdecode PUBLIC EMSCRIPTEN) target_compile_definitions(webpdspdecode PUBLIC EMSCRIPTEN)

View File

@ -464,7 +464,7 @@ AC_ARG_ENABLE([sdl],
@<:@default=auto@:>@])) @<:@default=auto@:>@]))
AS_IF([test "x$enable_sdl" != "xno"], [ AS_IF([test "x$enable_sdl" != "xno"], [
CLEAR_LIBVARS([SDL]) CLEAR_LIBVARS([SDL])
AC_PATH_PROGS([LIBSDL_CONFIG], [sdl-config]) AC_PATH_PROGS([LIBSDL_CONFIG], [sdl2-config])
if test -n "$LIBSDL_CONFIG"; then if test -n "$LIBSDL_CONFIG"; then
SDL_INCLUDES=`$LIBSDL_CONFIG --cflags` SDL_INCLUDES=`$LIBSDL_CONFIG --cflags`
SDL_LIBS="`$LIBSDL_CONFIG --libs`" SDL_LIBS="`$LIBSDL_CONFIG --libs`"
@ -474,13 +474,12 @@ AS_IF([test "x$enable_sdl" != "xno"], [
sdl_header="no" sdl_header="no"
LIBCHECK_PROLOGUE([SDL]) LIBCHECK_PROLOGUE([SDL])
AC_CHECK_HEADER([SDL/SDL.h], [sdl_header="SDL/SDL.h"], AC_CHECK_HEADER([SDL2/SDL.h], [sdl_header="SDL2/SDL.h"],
[AC_CHECK_HEADER([SDL.h], [sdl_header="SDL.h"], [AC_MSG_WARN(SDL2 library not available - no SDL.h)])
[AC_MSG_WARN(SDL library not available - no sdl.h)])])
if test x"$sdl_header" != "xno"; then if test x"$sdl_header" != "xno"; then
AC_LANG_PUSH(C) AC_LANG_PUSH(C)
SDL_SAVED_LIBS="$LIBS" SDL_SAVED_LIBS="$LIBS"
for lib in "" "-lSDL" "-lSDLmain -lSDL"; do for lib in "" "-lSDL2" "-lSDL2main -lSDL2"; do
LIBS="$SDL_SAVED_LIBS $lib" LIBS="$SDL_SAVED_LIBS $lib"
# Perform a full link to ensure SDL_main is resolved if needed. # Perform a full link to ensure SDL_main is resolved if needed.
AC_LINK_IFELSE( AC_LINK_IFELSE(

View File

@ -30,7 +30,7 @@
#if defined(WEBP_HAVE_JUST_SDL_H) #if defined(WEBP_HAVE_JUST_SDL_H)
#include <SDL.h> #include <SDL.h>
#else #else
#include <SDL/SDL.h> #include <SDL2/SDL.h>
#endif #endif
static void ProcessEvents(void) { static void ProcessEvents(void) {

View File

@ -20,88 +20,75 @@
#include "webp_to_sdl.h" #include "webp_to_sdl.h"
#include <stdio.h> #include <stdio.h>
#include "src/webp/decode.h" #include "src/webp/decode.h"
#if defined(WEBP_HAVE_JUST_SDL_H) #if defined(WEBP_HAVE_JUST_SDL_H)
#include <SDL.h> #include <SDL.h>
#else #else
#include <SDL/SDL.h> #include <SDL2/SDL.h>
#endif #endif
static int init_ok = 0; static int init_ok = 0;
int WebPToSDL(const char* data, unsigned int data_size) { int WebPToSDL(const char* data, unsigned int data_size) {
int ok = 0; int ok = 0;
VP8StatusCode status; VP8StatusCode status;
WebPDecoderConfig config; WebPBitstreamFeatures input;
WebPBitstreamFeatures* const input = &config.input; uint8_t* output = NULL;
WebPDecBuffer* const output = &config.output; SDL_Window* window = NULL;
SDL_Surface* screen = NULL; SDL_Renderer* renderer = NULL;
SDL_Surface* surface = NULL; SDL_Texture* texture = NULL;
int width, height;
if (!WebPInitDecoderConfig(&config)) {
fprintf(stderr, "Library version mismatch!\n");
return 0;
}
if (!init_ok) { if (!init_ok) {
SDL_Init(SDL_INIT_VIDEO); SDL_Init(SDL_INIT_VIDEO);
init_ok = 1; init_ok = 1;
} }
status = WebPGetFeatures((uint8_t*)data, (size_t)data_size, &config.input); status = WebPGetFeatures((uint8_t*)data, (size_t)data_size, &input);
if (status != VP8_STATUS_OK) goto Error; if (status != VP8_STATUS_OK) goto Error;
width = input.width;
height = input.height;
screen = SDL_SetVideoMode(input->width, input->height, 32, SDL_SWSURFACE); SDL_CreateWindowAndRenderer(width, height, 0, &window, &renderer);
if (screen == NULL) { if (window == NULL || renderer == NULL) {
fprintf(stderr, "Unable to set video mode (32bpp %dx%d)!\n", fprintf(stderr, "Unable to create window or renderer!\n");
input->width, input->height);
goto Error; goto Error;
} }
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY,
"linear"); // make the scaled rendering look smoother.
SDL_RenderSetLogicalSize(renderer, width, height);
surface = SDL_CreateRGBSurface(SDL_SWSURFACE, texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
input->width, input->height, 32, SDL_TEXTUREACCESS_STREAMING, width, height);
0x000000ffu, // R mask if (texture == NULL) {
0x0000ff00u, // G mask fprintf(stderr, "Unable to create %dx%d RGBA texture!\n", width, height);
0x00ff0000u, // B mask
0xff000000u); // A mask
if (surface == NULL) {
fprintf(stderr, "Unable to create %dx%d RGBA surface!\n",
input->width, input->height);
goto Error; goto Error;
} }
if (SDL_MUSTLOCK(surface)) SDL_LockSurface(surface);
#if SDL_BYTEORDER == SDL_BIG_ENDIAN #if SDL_BYTEORDER == SDL_BIG_ENDIAN
output->colorspace = MODE_BGRA; output = WebPDecodeBGRA((const uint8_t*)data, (size_t)data_size, &width,
&height);
#else #else
output->colorspace = MODE_RGBA; output = WebPDecodeRGBA((const uint8_t*)data, (size_t)data_size, &width,
&height);
#endif #endif
output->width = surface->w; if (output == NULL) {
output->height = surface->h;
output->u.RGBA.rgba = surface->pixels;
output->u.RGBA.stride = surface->pitch;
output->u.RGBA.size = surface->pitch * surface->h;
output->is_external_memory = 1;
status = WebPDecode((const uint8_t*)data, (size_t)data_size, &config);
if (status != VP8_STATUS_OK) {
fprintf(stderr, "Error decoding image (%d)\n", status); fprintf(stderr, "Error decoding image (%d)\n", status);
goto Error; goto Error;
} }
if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface); SDL_UpdateTexture(texture, NULL, output, width * sizeof(uint32_t));
if (SDL_BlitSurface(surface, NULL, screen, NULL) || SDL_RenderClear(renderer);
SDL_Flip(screen)) { SDL_RenderCopy(renderer, texture, NULL, NULL);
goto Error; SDL_RenderPresent(renderer);
}
ok = 1; ok = 1;
Error: Error:
SDL_FreeSurface(surface); // We should call SDL_DestroyWindow(window) but that makes .js fail.
SDL_FreeSurface(screen); SDL_DestroyRenderer(renderer);
WebPFreeDecBuffer(output); SDL_DestroyTexture(texture);
WebPFree(output);
return ok; return ok;
} }

View File

@ -37,13 +37,13 @@ else
endif endif
# SDL flags: use sdl-config if it exists # SDL flags: use sdl-config if it exists
SDL_CONFIG = $(shell sdl-config --version 2> /dev/null) SDL_CONFIG = $(shell sdl2-config --version 2> /dev/null)
ifneq ($(SDL_CONFIG),) ifneq ($(SDL_CONFIG),)
SDL_LIBS = $(shell sdl-config --libs) SDL_LIBS = $(shell sdl2-config --libs)
SDL_FLAGS = $(shell sdl-config --cflags) SDL_FLAGS = $(shell sdl2-config --cflags)
else else
# use best-guess # use best-guess
SDL_LIBS = -lSDL SDL_LIBS = -lSDL2
SDL_FLAGS = SDL_FLAGS =
endif endif

View File

@ -20,8 +20,7 @@ Emscripten and CMake.
```shell ```shell
cd webp_js && \ cd webp_js && \
emcmake cmake -DWEBP_BUILD_WEBP_JS=ON \ emcmake cmake -DWEBP_BUILD_WEBP_JS=ON ../
../
``` ```
- compile webp.js using 'emmake make'. - compile webp.js using 'emmake make'.
@ -55,27 +54,7 @@ directory.
See webp_js/index_wasm.html for a simple demo page using the WASM version of the See webp_js/index_wasm.html for a simple demo page using the WASM version of the
library. library.
You will need a fairly recent version of Emscripten (at least 2.0.18,
latest-upstream is recommended) and of your WASM-enabled browser to run this
version.
## Caveats ## Caveats
- First decoding using the library is usually slower, due to just-in-time - First decoding using the library is usually slower, due to just-in-time
compilation. compilation.
- Some versions of llvm produce the following compile error when SSE2 is
enabled.
```
"Unsupported: %516 = bitcast <8 x i16> %481 to i128
LLVM ERROR: BitCast Instruction not yet supported for integer types larger than 64 bits"
```
The corresponding Emscripten bug is at:
https://github.com/kripken/emscripten/issues/3788
Therefore, SSE2 optimization is currently disabled in CMakeLists.txt.
- If WEBP_ENABLE_SIMD is set to 1 the JavaScript version (webp.js) will be
disabled as wasm2js does not support SIMD.

View File

@ -9,6 +9,7 @@
noInitialRun : true noInitialRun : true
}; };
</script> </script>
<script src="./webp_wasm.js"></script>
<script type="text/javascript"> <script type="text/javascript">
'use strict'; 'use strict';
@ -16,40 +17,25 @@
// main wrapper for the function decoding a WebP into a canvas object // main wrapper for the function decoding a WebP into a canvas object
var WebpToCanvas; var WebpToCanvas;
function init() { Module.onRuntimeInitialized = async () => {
var xhr = new XMLHttpRequest(); // wrapper for the function decoding a WebP into a canvas object
xhr.open('GET', 'webp_wasm.wasm', true); WebpToCanvas = Module.cwrap('WebPToSDL', 'number', ['array', 'number']);
xhr.responseType = 'arraybuffer'; };
xhr.onload = function() {
Module.wasmBinary = xhr.response;
var script = document.createElement('script');
script.src = "webp_wasm.js";
document.body.appendChild(script);
};
xhr.send(null);
}
window.onload = init;
function decode(webp_data, canvas_id) { function decode(webp_data, canvas_id) {
var result; var result;
if (Module["asm"] != undefined) { // get the canvas to decode into
// wrapper for the function decoding a WebP into a canvas object var canvas = document.getElementById(canvas_id);
WebpToCanvas = Module.cwrap('WebPToSDL', 'number', ['array', 'number']); if (canvas == null) return;
// get the canvas to decode into // clear previous picture (if any)
var canvas = document.getElementById(canvas_id); Module.canvas = canvas;
if (canvas == null) return; canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
// clear previous picture (if any) // decode and measure timing
Module.canvas = canvas; var start = new Date();
canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height); var ret = WebpToCanvas(webp_data, webp_data.length);
// decode and measure timing var end = new Date();
var start = new Date(); var decodeTime = end - start;
var ret = WebpToCanvas(webp_data, webp_data.length); result = 'decoding time: ' + decodeTime +' ms.';
var end = new Date();
var decode_time = end - start;
result = 'decoding time: ' + decode_time +' ms.';
} else {
result = "WASM module not finished loading! Please retry";
}
// display timing result // display timing result
var speed_result = document.getElementById('timing'); var speed_result = document.getElementById('timing');
if (speed_result != null) { if (speed_result != null) {