diff --git a/.gitmodules b/.gitmodules index 813be11..42f27df 100644 --- a/.gitmodules +++ b/.gitmodules @@ -23,3 +23,6 @@ path = ext/ffmpeg url = https://github.com/FFmpeg/FFmpeg.git shallow = true +[submodule "ext/OpenHTJ2K"] + path = ext/OpenHTJ2K + url = https://github.com/osamu620/OpenHTJ2K diff --git a/CMakeLists.txt b/CMakeLists.txt index 56095e5..0fcd209 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,11 @@ add_library(libavutil STATIC IMPORTED) add_dependencies(libavutil ffmpeg) set_target_properties(libavutil PROPERTIES IMPORTED_LOCATION ${FFMPEG_INSTALL_DIR}/lib/libavutil.a) +# OpenHTJ2K support + +add_subdirectory(ext/OpenHTJ2K) +include_directories(ext/OpenHTJ2K/src/core/interface) + # md5 include_directories(ext/crypto-algorithms) @@ -83,7 +88,7 @@ add_library(md5 ext/crypto-algorithms/md5.c) file(GLOB LIBENCH_SRC_FILES src/main/cpp/*) add_executable(libench ${LIBENCH_SRC_FILES} ext/lodepng/lodepng.cpp) -target_link_libraries(libench openjph md5 jxl jxl_threads fast_lossless libavcodec libavutil ${KDU_LIBRARY} ${CMAKE_DL_LIBS}) +target_link_libraries(libench openjph md5 jxl jxl_threads fast_lossless libavcodec libavutil open_htj2k ${KDU_LIBRARY} ${CMAKE_DL_LIBS}) # tests @@ -99,3 +104,5 @@ add_test(NAME "j2k_1_kdu" COMMAND libench j2k_1_kdu ${PROJECT_SOURCE_DIR}/src/te add_test(NAME "png" COMMAND libench png ${PROJECT_SOURCE_DIR}/src/test/resources/images/test1.png) add_test(NAME "ffv1" COMMAND libench ffv1 ${PROJECT_SOURCE_DIR}/src/test/resources/images/test1.png) add_test(NAME "ffv1-rgba" COMMAND libench ffv1 ${PROJECT_SOURCE_DIR}/src/test/resources/images/rgba.png) +add_test(NAME "j2k_ht_ohtj2k" COMMAND libench j2k_ht_ohtj2k ${PROJECT_SOURCE_DIR}/src/test/resources/images/test1.png) +add_test(NAME "j2k_ht_ohtj2ka" COMMAND libench j2k_ht_ohtj2k ${PROJECT_SOURCE_DIR}/src/test/resources/images/rgba.png) diff --git a/ext/OpenHTJ2K b/ext/OpenHTJ2K new file mode 160000 index 0000000..5265cc6 --- /dev/null +++ b/ext/OpenHTJ2K @@ -0,0 +1 @@ +Subproject commit 5265cc6d8791dcafe429fb64083f040c14e69bb9 diff --git a/src/main/cpp/main.cpp b/src/main/cpp/main.cpp index 78af1e2..2c206f6 100644 --- a/src/main/cpp/main.cpp +++ b/src/main/cpp/main.cpp @@ -15,6 +15,7 @@ extern "C" { #include "kduht_codec.h" #include "png_codec.h" #include "ffv1_codec.h" +#include "ohtj2k_codec.h" #define STB_IMAGE_IMPLEMENTATION #define STBI_ONLY_PNG @@ -83,6 +84,9 @@ int main(int argc, char* argv[]) { } else if (result["codec"].as() == "qoi") { encoder.reset(new libench::QOIEncoder()); decoder.reset(new libench::QOIDecoder()); + } else if (result["codec"].as() == "j2k_ht_ohtj2k") { + encoder.reset(new libench::OHTJ2KEncoder()); + decoder.reset(new libench::OHTJ2KDecoder()); } else if (result["codec"].as() == "jxl") { encoder.reset(new libench::JXLEncoder<2>()); decoder.reset(new libench::JXLDecoder()); diff --git a/src/main/cpp/ohtj2k_codec.cpp b/src/main/cpp/ohtj2k_codec.cpp new file mode 100644 index 0000000..f5e3e34 --- /dev/null +++ b/src/main/cpp/ohtj2k_codec.cpp @@ -0,0 +1,166 @@ +#include "ohtj2k_codec.h" + +#include + +#include +#include + +constexpr uint32_t NUMTHREADS = 1; +/* + * OHTJ2KEncoder + */ + +libench::OHTJ2KEncoder::OHTJ2KEncoder() : num_threads(NUMTHREADS){}; + +libench::CodestreamBuffer libench::OHTJ2KEncoder::encodeRGB8( + const uint8_t* pixels, uint32_t width, uint32_t height) { + return this->encode8(pixels, width, height, 3); +} + +libench::CodestreamBuffer libench::OHTJ2KEncoder::encodeRGBA8( + const uint8_t* pixels, uint32_t width, uint32_t height) { + return this->encode8(pixels, width, height, 4); +} + +libench::CodestreamBuffer libench::OHTJ2KEncoder::encode8(const uint8_t* pixels, + uint32_t width, + uint32_t height, + uint8_t num_comps) { + // siz + open_htj2k::siz_params siz; + siz.Rsiz = 0; + siz.Xsiz = width; + siz.Ysiz = height; + siz.XOsiz = 0; + siz.YOsiz = 0; + siz.XTsiz = 0; + siz.YTsiz = 0; + siz.XTOsiz = 0; + siz.YTOsiz = 0; + siz.Csiz = num_comps; + for (auto c = 0; c < siz.Csiz; ++c) { + siz.Ssiz.push_back(7); + auto compw = width; + auto comph = height; + siz.XRsiz.push_back(((siz.Xsiz - siz.XOsiz) + compw - 1) / compw); + siz.YRsiz.push_back(((siz.Ysiz - siz.YOsiz) + comph - 1) / comph); + } + + // cod + open_htj2k::cod_params cod; + cod.blkwidth = 5; // 2^(5+2) = 128 + cod.blkheight = 3; // 2^(3+2) = 32 + cod.is_max_precincts = false; + cod.use_SOP = false; + cod.use_EPH = false; + cod.progression_order = 2; + cod.number_of_layers = 1; + cod.use_color_trafo = 1; + cod.dwt_levels = 6; + cod.codeblock_style = 0x040; + cod.transformation = 1; + + for (size_t i = 0; i < 6; ++i) { + cod.PPx.push_back(8); // 2^8 = 256 + cod.PPy.push_back(8); + } + cod.PPx.push_back(7); // 2^7 = 128 + cod.PPy.push_back(7); + + // qcd + open_htj2k::qcd_params qcd; + qcd.is_derived = false; + qcd.number_of_guardbits = 1; + qcd.base_step = 1.0 / 256; + if (qcd.base_step == 0.0) { + qcd.base_step = 1.0f / static_cast(1 << 8); + } + + bool isJPH = false; + uint8_t Qfactor = 0xff; // 0xff means no qfactor value is specified + uint8_t color_space = 0; // 0:RGB, 1:YCC + + auto buf = std::make_unique[]>(num_comps); + for (size_t i = 0; i < num_comps; ++i) { + buf[i] = std::make_unique(width * height); + } + std::vector input_buf; + for (auto c = 0; c < num_comps; ++c) { + input_buf.push_back(buf[c].get()); + } + const uint8_t* line = pixels; + for (uint32_t i = 0; i < height; ++i) { + for (uint32_t c = 0; c < num_comps; c++) { + const uint8_t* in = line + c; + int32_t* out = buf[c].get() + i * width; + for (uint32_t p = 0; p < width; p++) { + *out = *in; + out += 1; + in += num_comps; + } + } + line += num_comps * width; + } + open_htj2k::openhtj2k_encoder encoder("", input_buf, siz, cod, qcd, Qfactor, + isJPH, color_space, num_threads); + encoder.set_output_buffer(this->outbuf_); + + size_t total_size = total_size = encoder.invoke(); + + libench::CodestreamBuffer cb; + + cb.codestream = outbuf_.data(); + cb.size = total_size; + + return cb; +} + +/* + * OHTJ2KDecoder + */ + +libench::OHTJ2KDecoder::OHTJ2KDecoder() : num_threads(NUMTHREADS){}; + +libench::PixelBuffer libench::OHTJ2KDecoder::decodeRGB8( + const uint8_t* codestream, size_t size, uint32_t width, uint32_t height, + const uint8_t* init_data, size_t init_data_size) { + return this->decode8(codestream, size, 3); +} + +libench::PixelBuffer libench::OHTJ2KDecoder::decodeRGBA8( + const uint8_t* codestream, size_t size, uint32_t width, uint32_t height, + const uint8_t* init_data, size_t init_data_size) { + return this->decode8(codestream, size, 4); +} + +libench::PixelBuffer libench::OHTJ2KDecoder::decode8(const uint8_t* codestream, + size_t size, + uint8_t num_comps) { + open_htj2k::openhtj2k_decoder decoder(codestream, size, 0, num_threads); + std::vector buf; + std::vector img_width; + std::vector img_height; + std::vector img_depth; + std::vector img_signed; + decoder.invoke(buf, img_width, img_height, img_depth, img_signed); + uint32_t width = img_width[0]; + uint32_t height = img_height[0]; + this->pixels_.resize(width * height * num_comps); + for (uint32_t i = 0; i < height; ++i) { + uint8_t* line = &this->pixels_.data()[width * i * num_comps]; + for (uint32_t c = 0; c < num_comps; c++) { + uint8_t* out = line + c; + int32_t* in = buf[c] + width * i; + for (uint32_t p = 0; p < width; p++) { + *out = *in; + out += num_comps; + in += 1; + } + } + } + libench::PixelBuffer pb = {.height = height, + .width = width, + .num_comps = num_comps, + .pixels = &this->pixels_.data()[0]}; + return pb; +} \ No newline at end of file diff --git a/src/main/cpp/ohtj2k_codec.h b/src/main/cpp/ohtj2k_codec.h new file mode 100644 index 0000000..38c4210 --- /dev/null +++ b/src/main/cpp/ohtj2k_codec.h @@ -0,0 +1,50 @@ +#ifndef LIBENCH_OPENHTJ2K_H +#define LIBENCH_OPENHTJ2K_H + +#include + +#include "codec.h" +#include "decoder.hpp" +#include "encoder.hpp" + +namespace libench { + +class OHTJ2KEncoder : public Encoder { + public: + OHTJ2KEncoder(); + + virtual CodestreamBuffer encodeRGB8(const uint8_t* pixels, + const uint32_t width, uint32_t height); + + virtual CodestreamBuffer encodeRGBA8(const uint8_t* pixels, uint32_t width, + uint32_t height); + + private: + CodestreamBuffer encode8(const uint8_t* pixels, uint32_t width, + uint32_t height, uint8_t num_comps); + std::vector outbuf_; + uint32_t num_threads; +}; + +class OHTJ2KDecoder : public Decoder { + public: + OHTJ2KDecoder(); + + virtual PixelBuffer decodeRGB8(const uint8_t* codestream, size_t size, + uint32_t width, uint32_t height, + const uint8_t* init_data, + size_t init_data_size); + + virtual PixelBuffer decodeRGBA8(const uint8_t* codestream, size_t size, + uint32_t width, uint32_t height, + const uint8_t* init_data, + size_t init_data_size); + + private: + PixelBuffer decode8(const uint8_t* codestream, size_t size, + uint8_t num_comps); + std::vector pixels_; + uint32_t num_threads; +}; +} // namespace libench +#endif \ No newline at end of file