Skip to content

Commit

Permalink
uncompressed: implement generic compression decode
Browse files Browse the repository at this point in the history
  • Loading branch information
bradh committed Jul 9, 2024
1 parent 9f047ca commit c0e4d24
Show file tree
Hide file tree
Showing 32 changed files with 1,188 additions and 193 deletions.
11 changes: 10 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,18 @@ endif()
if (LIBSHARPYUV_FOUND)
list(APPEND REQUIRES_PRIVATE "libsharpyuv")
endif()
if (WITH_DEFLATE_HEADER_COMPRESSION)
if (WITH_DEFLATE_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC)
list(APPEND REQUIRES_PRIVATE "zlib")
endif()
if (WITH_UNCOMPRESSED_CODEC)
find_package(Brotli)
if (Brotli_FOUND)
message("Brotli found")
list(APPEND REQUIRES_PRIVATE "libbrotli")

This comment has been minimized.

Copy link
@kmilos

kmilos Jul 10, 2024

Contributor

It look like this should be libbrotlidec explicitly, as only the brotli/decode.h is currently being used AFAICT.

libbrotlidec in turn depends on libbrotlicommon already (as does libbrotlienc).

There is no libbrotli .pc file, so this is actually an error.

This comment has been minimized.

Copy link
@farindk

farindk Jul 10, 2024

Contributor

I assume that we will need the Brotli encoder also soon (right, @bradh ?)

This comment has been minimized.

Copy link
@kmilos

kmilos Jul 10, 2024

Contributor

Append libbrotlienc at that point as well then (you don't want to "overlink" when not necessary). In the meantime: #1236

This comment has been minimized.

Copy link
@farindk

farindk Jul 10, 2024

Contributor

BTW: Brotli is optional and not that important for typical use. If that blocks packaging, simply compile without until the next release.

This comment has been minimized.

Copy link
@bradh

bradh Jul 10, 2024

Author Contributor

I don't understand the part about pkg-config file, since I don't think I'm using that.

I assumed that if you had the decoder, you'd probably have the encoder (e.g. its the same -dev package), and that it would be less churn when I add the write support.

That said, if its an important change for you, I can add it when required.

This comment has been minimized.

Copy link
@kmilos

kmilos Jul 11, 2024

Contributor

I don't understand the part about pkg-config file

Not much to it: Requires.private (that we fill in via the REQUIRES_PRIVATE substitution here) can only reference other real .pc files. Brotli ships libbrotlidec.pc, libbrotlienc.pc, and libbrotlicommon.pc, there is no libbrotli.pc. Dec and enc ones already reference Requires.private: libbrotlicommon.

This comment has been minimized.

Copy link
@bradh

bradh Jul 11, 2024

Author Contributor

I don't understand the part about pkg-config file

Not much to it: Requires.private (that we fill in via the REQUIRES_PRIVATE substitution here) can only reference other real .pc files. Brotli ships libbrotlidec.pc, libbrotlienc.pc, and libbrotlicommon.pc, there is no libbrotli.pc. Dec and enc ones already reference Requires.private: libbrotlicommon.

I don't think I am using pkg-config. Is there something magical happening in the cmake incantations?

This comment has been minimized.

Copy link
@kmilos

kmilos Jul 11, 2024

Contributor

I don't think I am using pkg-config.

This is not for you. This is for constructing a functional libheif.pc so clients of the library can use it correctly via pkg-config (which both CMake and Meson, and other build systems rely on) in their projects.

This comment has been minimized.

Copy link
@kmilos

kmilos Jul 11, 2024

Contributor

I don't think I am using pkg-config.

You are, all over the place:

libfind_pkg_check_modules(AOM_PKGCONF aom)

libfind_pkg_check_modules(DAV1D_PKGCONF dav1d)

libfind_pkg_check_modules(LIBDE265_PKGCONF libde265)

etc.

This comment has been minimized.

Copy link
@bradh

bradh Jul 12, 2024

Author Contributor

I finally get it. Output for users, not input for build time dependencies.

else()
message("Brotli not found")
endif()
endif()

list(JOIN REQUIRES_PRIVATE " " REQUIRES_PRIVATE)

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ The placeholder `{codec}` can have these values: `LIBDE265`, `X265`, `AOM_DECODE

Further options are:

* `WITH_UNCOMPRESSED_CODEC`: enable support for uncompressed images according to ISO/IEC 23001-17:2023. This is *experimental*
and not available as a dynamic plugin.
* `WITH_UNCOMPRESSED_CODEC`: enable support for uncompressed images according to ISO/IEC 23001-17:2024. This is *experimental*
and not available as a dynamic plugin. When enabled, it adds a dependency to `zlib`, and optionally will use `brotli`.
* `WITH_DEFLATE_HEADER_COMPRESSION`: enables support for compressed metadata. When enabled, it adds a dependency to `zlib`.
Note that header compression is not widely supported yet.
* `WITH_LIBSHARPYUV`: enables high-quality YCbCr/RGB color space conversion algorithms (requires `libsharpyuv`,
Expand All @@ -170,7 +170,7 @@ Further options are:
* `PLUGIN_DIRECTORY`: the directory where libheif will search for dynamic plugins when the environment
variable `LIBHEIF_PLUGIN_PATH` is not set.
* `WITH_REDUCED_VISIBILITY`: only export those symbols into the library that are public API.
Has to be turned off for running the tests.
Has to be turned off for running some tests.

### macOS

Expand Down
26 changes: 26 additions & 0 deletions cmake/modules/FindBrotli.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
include(FindPackageHandleStandardArgs)

find_path(BROTLI_DEC_INCLUDE_DIR "brotli/decode.h")
find_path(BROTLI_ENC_INCLUDE_DIR "brotli/encode.h")

find_library(BROTLI_COMMON_LIB NAMES brotlicommon)
find_library(BROTLI_DEC_LIB NAMES brotlidec)
find_library(BROTLI_ENC_LIB NAMES brotlienc)

find_package_handle_standard_args(Brotli
FOUND_VAR
Brotli_FOUND
REQUIRED_VARS
BROTLI_COMMON_LIB
BROTLI_DEC_INCLUDE_DIR
BROTLI_DEC_LIB
BROTLI_ENC_INCLUDE_DIR
BROTLI_ENC_LIB
FAIL_MESSAGE
"Did not find Brotli"
)


set(HAVE_BROTLI ${Brotli_FOUND})
set(BROTLI_INCLUDE_DIRS ${BROTLI_DEC_INCLUDE_DIR} ${BROTLI_ENC_INCLUDE_DIR})
set(BROTLI_LIBS "${BROTLICOMMON_LIBRARY}" "${BROTLI_DEC_LIB}" "${BROTLI_ENC_LIB}")
6 changes: 6 additions & 0 deletions go/heif/heif.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,13 +269,17 @@ const (

SuberrorCameraExtrinsicMatrixUndefined = C.heif_suberror_Camera_extrinsic_matrix_undefined

SuberrorDecompressionInvalidData = C.heif_suberror_Decompression_invalid_data

// --- Memory_allocation_error ---

// A security limit preventing unreasonable memory allocations was exceeded by the input file.
// Please check whether the file is valid. If it is, contact us so that we could increase the
// security limits further.
SuberrorSecurityLimitExceeded = C.heif_suberror_Security_limit_exceeded

CompressionInitialisationError = C.heif_suberror_Compression_initialisation_error

// --- Usage_error ---

// An item ID was used that is not present in the file.
Expand Down Expand Up @@ -331,6 +335,8 @@ const (

SuberrorUnsupportedDataVersion = C.heif_suberror_Unsupported_data_version

SuberrorUnsupportedGenericCompressionMethod = C.heif_suberror_Unsupported_generic_compression_method

// The conversion of the source image to the requested chroma / colorspace is not supported.
SuberrorUnsupportedColorConversion = C.heif_suberror_Unsupported_color_conversion

Expand Down
17 changes: 14 additions & 3 deletions libheif/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ set(libheif_sources
init.h
logging.h
logging.cc
metadata_compression.cc
metadata_compression.h
compression.h
compression_brotli.cc
compression_zlib.cc
common_utils.cc
common_utils.h
region.cc
Expand Down Expand Up @@ -133,11 +134,21 @@ else ()
message("Not compiling 'libsharpyuv'")
endif ()

if (WITH_DEFLATE_HEADER_COMPRESSION)
if (WITH_DEFLATE_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC)
find_package(ZLIB REQUIRED)
target_link_libraries(heif PRIVATE ZLIB::ZLIB)
target_compile_definitions(heif PRIVATE WITH_ZLIB_COMPRESSION=1)
endif ()

if (WITH_DEFLATE_HEADER_COMPRESSION)
target_compile_definitions(heif PRIVATE WITH_DEFLATE_HEADER_COMPRESSION=1)
endif ()

if (HAVE_BROTLI)
target_compile_definitions(heif PUBLIC HAVE_BROTLI=1)
target_include_directories(heif PRIVATE ${BROTLI_INCLUDE_DIRS})
target_link_libraries(heif PRIVATE ${BROTLI_LIBS})
endif()

if (ENABLE_MULTITHREADING_SUPPORT)
find_package(Threads)
Expand Down
10 changes: 10 additions & 0 deletions libheif/api/libheif/heif.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,11 @@ enum heif_suberror_code

heif_suberror_No_vvcC_box = 141,

// icbr is only needed in some situations, this error is for those cases
heif_suberror_No_icbr_box = 142,

// Decompressing generic compression or header compression data failed (e.g. bitstream corruption)
heif_suberror_Decompression_invalid_data = 150,

// --- Memory_allocation_error ---

Expand All @@ -249,6 +254,9 @@ enum heif_suberror_code
// security limits further.
heif_suberror_Security_limit_exceeded = 1000,

// There was an error from the underlying compression / decompression library.
// One possibility is lack of resources (e.g. memory).
heif_suberror_Compression_initialisation_error = 1001,

// --- Usage_error ---

Expand Down Expand Up @@ -297,6 +305,8 @@ enum heif_suberror_code

heif_suberror_Unsupported_header_compression_method = 3005,

// Generically compressed data used an unsupported compression method
heif_suberror_Unsupported_generic_compression_method = 3006,

// --- Encoder_plugin_error ---

Expand Down
3 changes: 3 additions & 0 deletions libheif/api/libheif/heif_emscripten.h
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ EMSCRIPTEN_BINDINGS(libheif) {
emscripten::enum_<heif_suberror_code>("heif_suberror_code")
.value("heif_suberror_Unspecified", heif_suberror_Unspecified)
.value("heif_suberror_Cannot_write_output_data", heif_suberror_Cannot_write_output_data)
.value("heif_suberror_Compression_initialisation_error", heif_suberror_Compression_initialisation_error)
.value("heif_suberror_Decompression_invalid_data", heif_suberror_Decompression_invalid_data)
.value("heif_suberror_Encoder_initialization", heif_suberror_Encoder_initialization)
.value("heif_suberror_Encoder_encoding", heif_suberror_Encoder_encoding)
.value("heif_suberror_Encoder_cleanup", heif_suberror_Encoder_cleanup)
Expand Down Expand Up @@ -386,6 +388,7 @@ EMSCRIPTEN_BINDINGS(libheif) {
.value("heif_suberror_Unsupported_codec", heif_suberror_Unsupported_codec)
.value("heif_suberror_Unsupported_image_type", heif_suberror_Unsupported_image_type)
.value("heif_suberror_Unsupported_data_version", heif_suberror_Unsupported_data_version)
.value("heif_suberror_Unsupported_generic_compression_method", heif_suberror_Unsupported_generic_compression_method)
.value("heif_suberror_Unsupported_color_conversion", heif_suberror_Unsupported_color_conversion)
.value("heif_suberror_Unsupported_item_construction_method", heif_suberror_Unsupported_item_construction_method)
.value("heif_suberror_Unsupported_header_compression_method", heif_suberror_Unsupported_header_compression_method)
Expand Down
25 changes: 25 additions & 0 deletions libheif/bitstream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,31 @@ uint32_t BitstreamRange::read32()
(buf[3]));
}

uint64_t BitstreamRange::read64()
{
if (!prepare_read(8)) {
return 0;
}

uint8_t buf[8];

auto istr = get_istream();
bool success = istr->read((char*) buf, 8);

if (!success) {
set_eof_while_reading();
return 0;
}

return (uint64_t) (((uint64_t)buf[0] << 56) |
((uint64_t)buf[1] << 48) |
((uint64_t)buf[2] << 40) |
((uint64_t)buf[3] << 32) |
(buf[4] << 24) |
(buf[5] << 16) |
(buf[6] << 8) |
(buf[7]));
}

int32_t BitstreamRange::read32s()
{
Expand Down
2 changes: 2 additions & 0 deletions libheif/bitstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ class BitstreamRange

int32_t read32s();

uint64_t read64();

std::string read_string();

bool read(uint8_t* data, size_t n);
Expand Down
8 changes: 8 additions & 0 deletions libheif/box.cc
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,14 @@ Error Box::read(BitstreamRange& range, std::shared_ptr<Box>* result)
case fourcc("uncC"):
box = std::make_shared<Box_uncC>();
break;

case fourcc("cmpC"):
box = std::make_shared<Box_cmpC>();
break;

case fourcc("icbr"):
box = std::make_shared<Box_icbr>();
break;
#endif

// --- JPEG 2000
Expand Down
87 changes: 87 additions & 0 deletions libheif/codecs/uncompressed_box.cc
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ std::string Box_uncC::dump(Indent& indent) const
return sstr.str();
}


Error Box_uncC::write(StreamWriter& writer) const
{
size_t box_start = reserve_box_header_space(writer);
Expand Down Expand Up @@ -361,3 +362,89 @@ Error Box_uncC::write(StreamWriter& writer) const

return Error::Ok;
}


Error Box_cmpC::parse(BitstreamRange& range)
{
parse_full_box_header(range);
compression_type = range.read32();
uint8_t v = range.read8();
must_decompress_individual_entities = ((v & 0x80) == 0x80);
compressed_range_type = (v & 0x7f);
return range.get_error();
}


std::string Box_cmpC::dump(Indent& indent) const
{
std::ostringstream sstr;
sstr << Box::dump(indent);
sstr << indent << "compression_type: " << to_fourcc(compression_type) << "\n";
sstr << indent << "must_decompress_individual_entities: " << must_decompress_individual_entities << "\n";
sstr << indent << "compressed_entity_type: " << (int)compressed_range_type << "\n";
return sstr.str();
}

Error Box_cmpC::write(StreamWriter& writer) const
{
size_t box_start = reserve_box_header_space(writer);

writer.write32(compression_type);
uint8_t v = must_decompress_individual_entities ? 0x80 : 0x00;
v |= (compressed_range_type & 0x7F);
writer.write8(v);

prepend_header(writer, box_start);

return Error::Ok;
}


Error Box_icbr::parse(BitstreamRange& range)
{
parse_full_box_header(range);
uint32_t num_ranges = range.read32();
for (uint32_t r = 0; r < num_ranges; r++) {
struct ByteRange byteRange;
if (get_version() == 1) {
byteRange.range_offset = range.read64();
byteRange.range_size = range.read64();
} else if (get_version() == 0) {
byteRange.range_offset = range.read32();
byteRange.range_size = range.read32();
}
m_ranges.push_back(byteRange);
}
return range.get_error();
}


std::string Box_icbr::dump(Indent& indent) const
{
std::ostringstream sstr;
sstr << Box::dump(indent);
sstr << indent << "num_ranges: " << m_ranges.size() << "\n";
for (ByteRange range: m_ranges) {
sstr << indent << "range_offset: " << range.range_offset << ", range_size: " << range.range_size << "\n";
}
return sstr.str();
}

Error Box_icbr::write(StreamWriter& writer) const
{
size_t box_start = reserve_box_header_space(writer);

writer.write32((uint32_t)m_ranges.size());
for (ByteRange range: m_ranges) {
if (get_version() == 1) {
writer.write64(range.range_offset);
writer.write64(range.range_size);
} else if (get_version() == 0) {
writer.write32((uint32_t)range.range_offset);
writer.write32((uint32_t)range.range_size);
}
}
prepend_header(writer, box_start);

return Error::Ok;
}
Loading

0 comments on commit c0e4d24

Please sign in to comment.