Skip to content

Commit

Permalink
Wasm support (#160)
Browse files Browse the repository at this point in the history
* Experimental support of emscripten

* 'wasmnization' in progress

* initial workable version of 'wasmnized' library

* Fix a bug in wasm module for image size detection; Add file selector in index.html

* Fix compilation error on aarch64 with gcc

* Update CHANGELOG and open_htj2k_version.hpp

* Update CHANGELOG

* Small editorial changes

* Bump up version to 0.2.3

* Fix for MSVC
  • Loading branch information
osamu620 committed Dec 22, 2023
1 parent 354bd66 commit 2d57671
Show file tree
Hide file tree
Showing 8 changed files with 538 additions and 57 deletions.
111 changes: 62 additions & 49 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ else()
endif()

# Check Thread support
if (NOT EMSCRIPTEN)
find_package(Threads)

endif()
# Check TIFF support
find_package(TIFF)

Expand All @@ -70,7 +71,14 @@ if(NOT CMAKE_BUILD_TYPE)
endif()

# Compiler optimization settings
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") # MSVC
if(EMSCRIPTEN)
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -Wall -Wextra -Wconversion -Wsign-conversion -Wunused-parameter -Wformat=0 -fexceptions -fcolor-diagnostics"
)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -O0 -g")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -O3 -DNDEBUG")
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -O3 -g -DNDEBUG")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") # MSVC
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd5051")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} /Od /DDEBUG /ZI")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} /Ox")
Expand Down Expand Up @@ -108,39 +116,40 @@ else() # GCC
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -O3 -g -DNDEBUG")
endif()

if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^[xX]86_64$|^[aA][mM][dD]64$") # x86_64
if(NOT MINGW)
option(ENABLE_AVX2 "Enable the use of Intel AVX2 intrinsics" ON)
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} /arch:AVX2 /EHsc /D \"_CRT_SECURE_NO_WARNINGS\"")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU|IntelLLVM|Intel")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -mtune=native")
if(NOT EMSCRIPTEN)
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^[xX]86_64$|^[aA][mM][dD]64$") # x86_64
if(NOT MINGW)
option(ENABLE_AVX2 "Enable the use of Intel AVX2 intrinsics" ON)
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} /arch:AVX2 /EHsc /D \"_CRT_SECURE_NO_WARNINGS\"")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU|IntelLLVM|Intel")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native -mtune=native")
endif()
endif()
endif()

if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^[aA][rR][mM]64$|^[aA][aA][rR][cC][hH]64$") # aarch64
option(ENABLE_ARM_NEON "Enable the use of ARM NEON intrinsics" ON)
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} /EHsc /D \"_CRT_SECURE_NO_WARNINGS\"")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(APPLE)
message(STATUS "Added -mcpu=apple-m1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=apple-m1 -mtune=native")
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^[aA][rR][mM]64$|^[aA][aA][rR][cC][hH]64$") # aarch64
option(ENABLE_ARM_NEON "Enable the use of ARM NEON intrinsics" ON)
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} /EHsc /D \"_CRT_SECURE_NO_WARNINGS\"")
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if(APPLE)
message(STATUS "Added -mcpu=apple-m1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=apple-m1 -mtune=native")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a -mtune=cortex-a72")
endif()
else()
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -Wno-unused-parameter -Wno-unused-variable -Wformat-overflow=0 -Wno-unused-but-set-variable"
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a -mtune=cortex-a72")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flax-vector-conversions")
endif()
else()
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} -Wno-unused-parameter -Wno-unused-variable -Wformat-overflow=0 -Wno-unused-but-set-variable"
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a -mtune=cortex-a72")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flax-vector-conversions")
endif()
endif()

# add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/source/thirdparty/highway
# EXCLUDE_FROM_ALL)

Expand All @@ -159,29 +168,31 @@ configure_file(

# Source files settings "libopen_htj2k" shared library target
add_library(open_htj2k ${SOURCES})
if (Threads_FOUND)
message(STATUS "Thread library found")
target_compile_definitions(open_htj2k PUBLIC "OPENHTJ2K_THREAD")
message(STATUS "OPENHTJ2K_THREAD is set")
endif()
if (NOT EMSCRIPTEN)
if (Threads_FOUND)
message(STATUS "Thread library found")
target_compile_definitions(open_htj2k PUBLIC "OPENHTJ2K_THREAD")
message(STATUS "OPENHTJ2K_THREAD is set")
endif()

if (TIFF_FOUND)
message(STATUS "libtiff found")
message(STATUS "tiff include = ${TIFF_INCLUDE_DIR}")
message(STATUS "tiff library = ${TIFF_LIBRARIES}")
target_compile_definitions(open_htj2k PUBLIC "OPENHTJ2K_TIFF_SUPPORT")
target_include_directories(open_htj2k PUBLIC ${TIFF_INCLUDE_DIR})
target_link_libraries(open_htj2k PUBLIC ${TIFF_LIBRARIES})
message(STATUS "OPENHTJ2K_TIFF_SUPPORT is set ")
set(PKG_CONFIG_REQUIRES "libtiff")
endif()
if (TIFF_FOUND)
message(STATUS "libtiff found")
message(STATUS "tiff include = ${TIFF_INCLUDE_DIR}")
message(STATUS "tiff library = ${TIFF_LIBRARIES}")
target_compile_definitions(open_htj2k PUBLIC "OPENHTJ2K_TIFF_SUPPORT")
target_include_directories(open_htj2k PUBLIC ${TIFF_INCLUDE_DIR})
target_link_libraries(open_htj2k PUBLIC ${TIFF_LIBRARIES})
message(STATUS "OPENHTJ2K_TIFF_SUPPORT is set ")
set(PKG_CONFIG_REQUIRES "libtiff")
endif()

if(ENABLE_ARM_NEON)
message(STATUS "OPENHTJ2K_ENABLE_ARM_NEON is set")
target_compile_definitions(open_htj2k PUBLIC "OPENHTJ2K_ENABLE_ARM_NEON")
elseif(ENABLE_AVX2)
message(STATUS "OPENHTJ2K_TRY_AVX2 is set")
target_compile_definitions(open_htj2k PUBLIC "OPENHTJ2K_TRY_AVX2")
if(ENABLE_ARM_NEON)
message(STATUS "OPENHTJ2K_ENABLE_ARM_NEON is set")
target_compile_definitions(open_htj2k PUBLIC "OPENHTJ2K_ENABLE_ARM_NEON")
elseif(ENABLE_AVX2)
message(STATUS "OPENHTJ2K_TRY_AVX2 is set")
target_compile_definitions(open_htj2k PUBLIC "OPENHTJ2K_TRY_AVX2")
endif()
endif()
target_include_directories(
open_htj2k
Expand All @@ -198,9 +209,11 @@ set_target_properties(
open_htj2k PROPERTIES OUTPUT_NAME
$<IF:$<CONFIG:Debug>,open_htj2k_d,open_htj2k_R>)
# target_link_libraries(open_htj2k PUBLIC ${CMAKE_THREAD_LIBS_INIT} PRIVATE hwy)
if (NOT EMSCRIPTEN)
if (Threads_FOUND)
target_link_libraries(open_htj2k PUBLIC ${CMAKE_THREAD_LIBS_INIT})
endif()
endif()

add_subdirectory(source/core/codestream)
add_subdirectory(source/core/coding)
Expand Down
1 change: 1 addition & 0 deletions source/apps/decoder/main_dec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ int main(int argc, char *argv[]) {
img_signed.clear();
// invoke decoding
try {
decoder.parse();
decoder.invoke(buf, img_width, img_height, img_depth, img_signed);
} catch (std::exception &exc) {
return EXIT_FAILURE;
Expand Down
3 changes: 2 additions & 1 deletion source/core/common/open_htj2k_version.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@

#define OPENHTJ2K_VERSION_MAJOR 0
#define OPENHTJ2K_VERSION_MINOR 2
#define OPENHTJ2K_VERSION_PATCH 2
#define OPENHTJ2K_VERSION_PATCH 3

100 changes: 93 additions & 7 deletions source/core/interface/decoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,36 @@ namespace open_htj2k {
class openhtj2k_decoder_impl {
private:
j2c_src_memory in;
const uint8_t reduce_NL;
uint8_t reduce_NL;
bool is_codestream_set;
bool is_parsed;
j2k_main_header main_header;

public:
openhtj2k_decoder_impl();
openhtj2k_decoder_impl(const char *, uint8_t reduce_NL, uint32_t num_threads);
openhtj2k_decoder_impl(const uint8_t *, size_t, uint8_t reduce_NL, uint32_t num_threads);
~openhtj2k_decoder_impl();
void init(const uint8_t *, size_t, uint8_t reduce_NL, uint32_t num_threads);
void parse();
uint16_t get_num_component();
uint32_t get_component_width(uint16_t);
uint32_t get_component_height(uint16_t);
uint8_t get_component_depth(uint16_t);
bool get_component_signedness(uint16_t);

void invoke(std::vector<int32_t *> &, std::vector<uint32_t> &, std::vector<uint32_t> &,
std::vector<uint8_t> &, std::vector<bool> &);
};

openhtj2k_decoder_impl::openhtj2k_decoder_impl() {
reduce_NL = 0;
is_codestream_set = false;
is_parsed = false;
}

openhtj2k_decoder_impl::openhtj2k_decoder_impl(const char *filename, const uint8_t r, uint32_t num_threads)
: reduce_NL(r) {
: reduce_NL(r), is_codestream_set(false), is_parsed(false) {
uintmax_t file_size;
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || __cplusplus >= 201703L)
try {
Expand Down Expand Up @@ -83,11 +101,12 @@ openhtj2k_decoder_impl::openhtj2k_decoder_impl(const char *filename, const uint8
throw std::exception();
}
fclose(fp);
is_codestream_set = true;
}

openhtj2k_decoder_impl::openhtj2k_decoder_impl(const uint8_t *buf, const size_t length, const uint8_t r,
uint32_t num_threads)
: reduce_NL(r) {
: reduce_NL(r), is_codestream_set(false), is_parsed(false) {
if (buf == nullptr) {
}
#ifdef OPENHTJ2K_THREAD
Expand All @@ -97,15 +116,66 @@ openhtj2k_decoder_impl::openhtj2k_decoder_impl(const uint8_t *buf, const size_t
in.alloc_memory(static_cast<uint32_t>(length));
uint8_t *p = in.get_buf_pos();
memcpy(p, buf, length);
is_codestream_set = true;
}

void openhtj2k_decoder_impl::invoke(std::vector<int32_t *> &buf, std::vector<uint32_t> &width,
std::vector<uint32_t> &height, std::vector<uint8_t> &depth,
std::vector<bool> &is_signed) {
void openhtj2k_decoder_impl::init(const uint8_t *buf, const size_t length, const uint8_t r,
uint32_t num_threads) {
reduce_NL = r;
if (buf == nullptr) {
}
#ifdef OPENHTJ2K_THREAD
ThreadPool::instance(num_threads);
#endif
// open codestream and store it in memory
in.alloc_memory(static_cast<uint32_t>(length));
uint8_t *p = in.get_buf_pos();
memcpy(p, buf, length);
is_codestream_set = true;
}

void openhtj2k_decoder_impl::parse() {
if (is_codestream_set == false) {
printf(
"ERROR: openhtj2k_decoder_impl::openhtj2k_decoder_impl() shall be called before calling "
"openhtj2k_decoder_impl::parse().\n");
throw std::exception();
}
// Read main header
j2k_main_header main_header;
main_header.read(in);
in.rewind_2bytes();
is_parsed = true;
}

uint16_t openhtj2k_decoder_impl::get_num_component() { return main_header.SIZ->get_num_components(); }
uint32_t openhtj2k_decoder_impl::get_component_width(uint16_t c) {
// Currently does not return component specific width
element_siz size, origin;
main_header.SIZ->get_image_size(size);
main_header.SIZ->get_image_origin(origin);

return size.x - origin.x;
}
uint32_t openhtj2k_decoder_impl::get_component_height(uint16_t c) {
// Currently does not return component specific height
element_siz size, origin;
main_header.SIZ->get_image_size(size);
main_header.SIZ->get_image_origin(origin);

return size.y - origin.y;
}
uint8_t openhtj2k_decoder_impl::get_component_depth(uint16_t c) { return main_header.SIZ->get_bitdepth(c); }
bool openhtj2k_decoder_impl::get_component_signedness(uint16_t c) { return main_header.SIZ->is_signed(c); }

void openhtj2k_decoder_impl::invoke(std::vector<int32_t *> &buf, std::vector<uint32_t> &width,
std::vector<uint32_t> &height, std::vector<uint8_t> &depth,
std::vector<bool> &is_signed) {
if (is_parsed == false) {
printf(
"ERROR: openhtj2k_decoder_impl::parse() shall be called before calling "
"openhtj2k_decoder_impl::invoke().\n");
throw std::exception();
}
element_siz numTiles;
main_header.get_number_of_tiles(numTiles.x, numTiles.y);
// printf("Tile num x = %d, y = %d\n", numTiles.x, numTiles.y);
Expand Down Expand Up @@ -172,6 +242,7 @@ openhtj2k_decoder_impl::~openhtj2k_decoder_impl() {
}

// public interface
openhtj2k_decoder::openhtj2k_decoder() { this->impl = MAKE_UNIQUE<openhtj2k_decoder_impl>(); }
openhtj2k_decoder::openhtj2k_decoder(const char *fname, const uint8_t reduce_NL, uint32_t num_threads) {
this->impl = MAKE_UNIQUE<openhtj2k_decoder_impl>(fname, reduce_NL, num_threads);
}
Expand All @@ -180,10 +251,25 @@ openhtj2k_decoder::openhtj2k_decoder(const uint8_t *buf, size_t length, const ui
uint32_t num_threads) {
this->impl = MAKE_UNIQUE<openhtj2k_decoder_impl>(buf, length, reduce_NL, num_threads);
}
void openhtj2k_decoder::init(const uint8_t *buf, size_t length, const uint8_t reduce_NL,
uint32_t num_threads) {
this->impl->init(buf, length, reduce_NL, num_threads);
}
void openhtj2k_decoder::parse() { this->impl->parse(); }

uint16_t openhtj2k_decoder::get_num_component() { return this->impl->get_num_component(); }
uint32_t openhtj2k_decoder::get_component_width(uint16_t c) { return this->impl->get_component_width(c); }
uint32_t openhtj2k_decoder::get_component_height(uint16_t c) { return this->impl->get_component_height(c); }
uint8_t openhtj2k_decoder::get_component_depth(uint16_t c) { return this->impl->get_component_depth(c); }
bool openhtj2k_decoder::get_component_signedness(uint16_t c) {
return this->impl->get_component_signedness(c);
}

void openhtj2k_decoder::invoke(std::vector<int32_t *> &buf, std::vector<uint32_t> &width,
std::vector<uint32_t> &height, std::vector<uint8_t> &depth,
std::vector<bool> &is_signed) {
this->impl->invoke(buf, width, height, depth, is_signed);
}

openhtj2k_decoder::~openhtj2k_decoder() = default;
} // namespace open_htj2k
16 changes: 16 additions & 0 deletions source/core/interface/decoder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,31 @@ class openhtj2k_decoder {

public:
#if defined(_MSC_VER) && !defined(OHTJ2K_STATIC)
__declspec(dllexport) openhtj2k_decoder();
__declspec(dllexport) openhtj2k_decoder(const char *, const uint8_t reduce_NL, uint32_t num_threads);
__declspec(dllexport)
openhtj2k_decoder(const uint8_t *, size_t, const uint8_t reduce_NL, uint32_t num_threads);
__declspec(dllexport) void init(const uint8_t *, size_t, uint8_t reduce_NL, uint32_t num_threads);
__declspec(dllexport) void parse();
__declspec(dllexport) uint16_t get_num_component();
__declspec(dllexport) uint32_t get_component_width(uint16_t);
__declspec(dllexport) uint32_t get_component_height(uint16_t);
__declspec(dllexport) uint8_t get_component_depth(uint16_t);
__declspec(dllexport) bool get_component_signedness(uint16_t);
__declspec(dllexport) void invoke(std::vector<int32_t *> &, std::vector<uint32_t> &,
std::vector<uint32_t> &, std::vector<uint8_t> &, std::vector<bool> &);
__declspec(dllexport) ~openhtj2k_decoder();
#else
openhtj2k_decoder();
openhtj2k_decoder(const char *, uint8_t reduce_NL, uint32_t num_threads);
openhtj2k_decoder(const uint8_t *, size_t, uint8_t reduce_NL, uint32_t num_threads);
void init(const uint8_t *, size_t, uint8_t reduce_NL, uint32_t num_threads);
void parse();
uint16_t get_num_component();
uint32_t get_component_width(uint16_t);
uint32_t get_component_height(uint16_t);
uint8_t get_component_depth(uint16_t);
bool get_component_signedness(uint16_t);
void invoke(std::vector<int32_t *> &, std::vector<uint32_t> &, std::vector<uint32_t> &,
std::vector<uint8_t> &, std::vector<bool> &);
~openhtj2k_decoder();
Expand Down
16 changes: 16 additions & 0 deletions subprojects/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.13)

set(CMAKE_SYSTEM_NAME Generic)

project (
open_htj2k
LANGUAGES CXX
)

set(CMAKE_LIBRARY_OUTput_DIRECTORY ${CMAKE_BINARY_DIR}/html)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/html)

add_subdirectory(".." open_htj2k EXCLUDE_FROM_ALL)
add_executable(libopen_htj2k "src/wrapper.cpp")
set_target_properties(libopen_htj2k PROPERTIES SUFFIX ".js" LINK_FLAGS "-03 -s WASM=1 -s EXPORT_ES6=1 -s MODULARIZE=1 -s ENVIRONMENT=web -s EXPORTED_FUNCTIONS=[_free,_malloc] -s EXPORTED_RUNTIME_METHODS=[ccall,cwrap,writeArrayToMemory,getValue] -s NO_EXIT_RUNTIME=1 -s ALLOW_MEMORY_GROWTH=1 -sASSERTIONS")
target_link_libraries(libopen_htj2k PRIVATE open_htj2k)
Loading

0 comments on commit 2d57671

Please sign in to comment.