diff --git a/src/lib/openjp2/BufferStream.c b/src/lib/openjp2/BufferStream.c new file mode 100644 index 000000000..969cf6b31 --- /dev/null +++ b/src/lib/openjp2/BufferStream.c @@ -0,0 +1,104 @@ +// Copyright (c) Chris Hafey. +// SPDX-License-Identifier: MIT +#include "BufferStream.h" +#include +#include "cio.h" + + + +OPJ_SIZE_T +opj_read_from_buffer (void* pdst, OPJ_SIZE_T len, opj_buffer_info_t* psrc) +{ + OPJ_SIZE_T n = (OPJ_SIZE_T) (psrc->buf + psrc->len - psrc->cur); + + if (n) { + if (n > len) + n = len; + + memcpy (pdst, psrc->cur, n); + psrc->cur += n; + } + else + n = (OPJ_SIZE_T)-1; + + return n; +} + +OPJ_SIZE_T +opj_write_to_buffer (void* p_buffer, OPJ_SIZE_T p_nb_bytes, + opj_buffer_info_t* p_source_buffer) +{ + + memcpy (p_source_buffer->cur, p_buffer, p_nb_bytes); + p_source_buffer->cur += p_nb_bytes; + + return p_nb_bytes; +} + +OPJ_SIZE_T +opj_skip_from_buffer (OPJ_SIZE_T len, opj_buffer_info_t* psrc) +{ + + OPJ_SIZE_T n = psrc->buf + psrc->len - psrc->cur; + + + if (n) { + if (n > len) + { + n = len; + } + psrc->cur += len; + + } + else + { + n = (OPJ_SIZE_T)-1; + } + return n; +} + +OPJ_BOOL +opj_seek_from_buffer (OPJ_OFF_T len, opj_buffer_info_t* psrc) +{ + OPJ_SIZE_T n = psrc->len; + + if (n > len) + n = len; + + psrc->cur = psrc->buf + n; + + return OPJ_TRUE; +} + +opj_stream_t* OPJ_CALLCONV +opj_stream_create_buffer_stream (opj_buffer_info_t* psrc, OPJ_BOOL input) +{ + opj_stream_t* ps; + if (!psrc) + return 0; + + ps = opj_stream_default_create (input); + + if (0 == ps) + return 0; + + opj_stream_set_user_data (ps, psrc, 0); + opj_stream_set_user_data_length (ps, psrc->len); + + if (input) + opj_stream_set_read_function ( + ps, (opj_stream_read_fn)opj_read_from_buffer); + else + opj_stream_set_write_function( + ps,(opj_stream_write_fn) opj_write_to_buffer); + + opj_stream_set_skip_function ( + ps, (opj_stream_skip_fn)opj_skip_from_buffer); + opj_stream_set_use_buffered_stream ( + ps, OPJ_TRUE); + + opj_stream_set_seek_function ( + ps, (opj_stream_seek_fn)opj_seek_from_buffer); + + return ps; +} diff --git a/src/lib/openjp2/BufferStream.h b/src/lib/openjp2/BufferStream.h new file mode 100644 index 000000000..c2e3baeb9 --- /dev/null +++ b/src/lib/openjp2/BufferStream.h @@ -0,0 +1,34 @@ +// +// Created by skinner on 5/8/25. +// + +#pragma once +#include "openjpeg.h" + +typedef struct opj_buffer_info { + OPJ_BYTE* buf; + OPJ_BYTE* cur; + OPJ_SIZE_T len; +} opj_buffer_info_t; +#ifdef __cplusplus +extern "C" { +#endif + OPJ_SIZE_T + opj_read_from_buffer (void* pdst, OPJ_SIZE_T len, opj_buffer_info_t* psrc); + + OPJ_SIZE_T + opj_write_to_buffer (void* p_buffer, OPJ_SIZE_T p_nb_bytes, + opj_buffer_info_t* p_source_buffer); + OPJ_SIZE_T + opj_skip_from_buffer (OPJ_SIZE_T len, opj_buffer_info_t* psrc); + + OPJ_BOOL + opj_seek_from_buffer (OPJ_OFF_T len, opj_buffer_info_t* psrc); + + opj_stream_t* OPJ_CALLCONV + opj_stream_create_buffer_stream (opj_buffer_info_t* psrc, OPJ_BOOL input); + void OPJ_CALLCONV opj_stream_set_use_buffered_stream(opj_stream_t* p_stream, + int use_buffered_stream); +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/src/lib/openjp2/CMakeLists.txt b/src/lib/openjp2/CMakeLists.txt index e65cb9d1a..bcad24bce 100644 --- a/src/lib/openjp2/CMakeLists.txt +++ b/src/lib/openjp2/CMakeLists.txt @@ -57,6 +57,8 @@ set(OPENJPEG_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/opj_stdint.h ${CMAKE_CURRENT_SOURCE_DIR}/sparse_array.c ${CMAKE_CURRENT_SOURCE_DIR}/sparse_array.h + ${CMAKE_CURRENT_SOURCE_DIR}/BufferStream.c + ${CMAKE_CURRENT_SOURCE_DIR}/BufferStream.h ) if(BUILD_JPIP) add_definitions(-DUSE_JPIP) diff --git a/src/lib/openjp2/cio.c b/src/lib/openjp2/cio.c index 4fde9fe23..08cecda1a 100644 --- a/src/lib/openjp2/cio.c +++ b/src/lib/openjp2/cio.c @@ -38,7 +38,72 @@ */ #include "opj_includes.h" +#include "BufferStream.h" +void OPJ_CALLCONV opj_stream_set_use_buffered_stream(opj_stream_t* p_stream, + OPJ_BOOL use_buffered_stream) +{ + opj_stream_private_t* l_stream = (opj_stream_private_t*) p_stream; + + if (! l_stream) { + return; + } + printf("set m_use_buffered_stream to %d\n",use_buffered_stream); + l_stream->m_use_buffered_stream = use_buffered_stream; +} +OPJ_SIZE_T skip_stream(opj_stream_private_t * stream, OPJ_SIZE_T len, opj_buffer_info_t* psrc) +{ + OPJ_SIZE_T current; + if (stream->m_use_buffered_stream) { + current = opj_skip_from_buffer(len, psrc); + } + else + { + current = (OPJ_SIZE_T) stream->m_skip_fn((OPJ_OFF_T)len, psrc); + } + return current; +} + +OPJ_SIZE_T read_stream(opj_stream_private_t * stream, void* pdst,OPJ_SIZE_T len, opj_buffer_info_t* psrc) +{ + OPJ_SIZE_T current; + if (stream->m_use_buffered_stream) + { + current = opj_read_from_buffer(pdst,len,psrc); + } + else + { + current = stream->m_read_fn(pdst, len, psrc); + } + return current; +} +OPJ_SIZE_T write_stream(opj_stream_private_t * stream, void* pbuffer, OPJ_SIZE_T p_nb_bytes, + opj_buffer_info_t* p_source_buffer) { + OPJ_SIZE_T current; + if (stream->m_use_buffered_stream) + { + current = opj_write_to_buffer(pbuffer,p_nb_bytes, + p_source_buffer); + } + else + { + current = stream->m_write_fn(pbuffer,p_nb_bytes,p_source_buffer); + } + return current; +} +OPJ_BOOL seek_stream(opj_stream_private_t * stream, OPJ_OFF_T len,opj_buffer_info_t* psrc) +{ + OPJ_BOOL success; + if (stream->m_use_buffered_stream) + { + success = opj_seek_from_buffer(len, psrc); + } + else + { + success = stream->m_seek_fn(len, psrc); + } + return success; +}; /* ----------------------------------------------------------------------- */ @@ -165,6 +230,8 @@ opj_stream_t* OPJ_CALLCONV opj_stream_create(OPJ_SIZE_T p_buffer_size, } l_stream->m_buffer_size = p_buffer_size; + // default the cio to byte-straem mode + l_stream->m_use_buffered_stream = OPJ_TRUE; l_stream->m_stored_data = (OPJ_BYTE *) opj_malloc(p_buffer_size); if (! l_stream->m_stored_data) { opj_free(l_stream); @@ -321,7 +388,7 @@ OPJ_SIZE_T opj_stream_read_data(opj_stream_private_t * p_stream, /* we should read less than a chunk -> read a chunk */ if (p_size < p_stream->m_buffer_size) { /* we should do an actual read on the media */ - p_stream->m_bytes_in_buffer = p_stream->m_read_fn(p_stream->m_stored_data, + p_stream->m_bytes_in_buffer = read_stream(p_stream, p_stream->m_stored_data, p_stream->m_buffer_size, p_stream->m_user_data); if (p_stream->m_bytes_in_buffer == (OPJ_SIZE_T) - 1) { @@ -351,7 +418,7 @@ OPJ_SIZE_T opj_stream_read_data(opj_stream_private_t * p_stream, } } else { /* direct read on the dest buffer */ - p_stream->m_bytes_in_buffer = p_stream->m_read_fn(p_buffer, p_size, + p_stream->m_bytes_in_buffer = read_stream(p_stream, p_buffer, p_size, p_stream->m_user_data); if (p_stream->m_bytes_in_buffer == (OPJ_SIZE_T) - 1) { @@ -440,7 +507,7 @@ OPJ_BOOL opj_stream_flush(opj_stream_private_t * p_stream, while (p_stream->m_bytes_in_buffer) { /* we should do an actual write on the media */ - l_current_write_nb_bytes = p_stream->m_write_fn(p_stream->m_current_data, + l_current_write_nb_bytes = write_stream(p_stream, p_stream->m_current_data, p_stream->m_bytes_in_buffer, p_stream->m_user_data); @@ -517,7 +584,7 @@ OPJ_OFF_T opj_stream_read_skip(opj_stream_private_t * p_stream, } /* we should do an actual skip on the media */ - l_current_skip_nb_bytes = p_stream->m_skip_fn(p_size, p_stream->m_user_data); + l_current_skip_nb_bytes = skip_stream(p_stream, p_size, p_stream->m_user_data); if (l_current_skip_nb_bytes == (OPJ_OFF_T) - 1) { opj_event_msg(p_event_mgr, EVT_INFO, "Stream reached its end !\n"); @@ -557,7 +624,7 @@ OPJ_OFF_T opj_stream_write_skip(opj_stream_private_t * p_stream, while (p_size > 0) { /* we should do an actual skip on the media */ - l_current_skip_nb_bytes = p_stream->m_skip_fn(p_size, p_stream->m_user_data); + l_current_skip_nb_bytes = skip_stream(p_stream, p_size, p_stream->m_user_data); if (l_current_skip_nb_bytes == (OPJ_OFF_T) - 1) { opj_event_msg(p_event_mgr, EVT_INFO, "Stream error!\n"); @@ -604,7 +671,7 @@ OPJ_BOOL opj_stream_read_seek(opj_stream_private_t * p_stream, OPJ_OFF_T p_size, p_stream->m_current_data = p_stream->m_stored_data; p_stream->m_bytes_in_buffer = 0; - if (!(p_stream->m_seek_fn(p_size, p_stream->m_user_data))) { + if (!(seek_stream(p_stream, p_size, p_stream->m_user_data))) { p_stream->m_status |= OPJ_STREAM_STATUS_END; return OPJ_FALSE; } else { @@ -628,7 +695,7 @@ OPJ_BOOL opj_stream_write_seek(opj_stream_private_t * p_stream, p_stream->m_current_data = p_stream->m_stored_data; p_stream->m_bytes_in_buffer = 0; - if (! p_stream->m_seek_fn(p_size, p_stream->m_user_data)) { + if (! seek_stream(p_stream, p_size, p_stream->m_user_data)) { p_stream->m_status |= OPJ_STREAM_STATUS_ERROR; return OPJ_FALSE; } else { diff --git a/src/lib/openjp2/cio.h b/src/lib/openjp2/cio.h index 7caee30af..327e09a96 100644 --- a/src/lib/openjp2/cio.h +++ b/src/lib/openjp2/cio.h @@ -50,6 +50,7 @@ The functions in CIO.C have for goal to realize a byte input / output process. /*@{*/ #include "opj_config_private.h" +#include "BufferStream.h" /* ----------------------------------------------------------------------- */ @@ -160,6 +161,12 @@ typedef struct opj_stream_private { * Used with OPJ_STREAM_STATUS_* defines. */ OPJ_UINT32 m_status; + /** + for wasm, don't store function pointer, rather than + function pointer. + */ + + OPJ_BOOL m_use_buffered_stream; } opj_stream_private_t; @@ -397,11 +404,28 @@ OPJ_SIZE_T opj_stream_default_write(void * p_buffer, OPJ_SIZE_T p_nb_bytes, */ OPJ_OFF_T opj_stream_default_skip(OPJ_OFF_T p_nb_bytes, void * p_user_data); +void OPJ_CALLCONV opj_stream_set_use_buffered_stream(opj_stream_t* p_stream, + OPJ_BOOL use_buffered_stream); /** * FIXME DOC. */ OPJ_BOOL opj_stream_default_seek(OPJ_OFF_T p_nb_bytes, void * p_user_data); +/** + * The skip, read, write, and seek operations on the input stream + * are abstracted within the following skip/read/write/seek_stream functions. + * The use of pointer functions were used earlier, but when compiling to + * web-assembly, the pointer functions did not work. The web-assembly code + * is used only with buffers to date. Therefore within these 4 functions, + * the explicit call to the buffer type stream is performed when m_use_buffered_stream + * is true. Otherwise the pointer functions are used, and the option to + * introduce other types of streams beyond file streams remains. + */ +OPJ_SIZE_T skip_stream(opj_stream_private_t * stream,OPJ_SIZE_T len, opj_buffer_info_t* psrc); +OPJ_SIZE_T read_stream(opj_stream_private_t * stream, void* pcst,OPJ_SIZE_T len, opj_buffer_info_t* psrc); +OPJ_SIZE_T write_stream(opj_stream_private_t * stream, void* pbuffer, OPJ_SIZE_T p_nb_bytes, + opj_buffer_info_t* p_source_buffer); +OPJ_BOOL seek_stream(opj_stream_private_t * stream, OPJ_OFF_T len,opj_buffer_info_t* psrc); /* ----------------------------------------------------------------------- */ /*@}*/