Skip to content

Commit 4efde38

Browse files
committed
uncompressed: implement generic compression decode
1 parent 32dc21b commit 4efde38

32 files changed

+1188
-193
lines changed

CMakeLists.txt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,9 +395,18 @@ endif()
395395
if (LIBSHARPYUV_FOUND)
396396
list(APPEND REQUIRES_PRIVATE "libsharpyuv")
397397
endif()
398-
if (WITH_DEFLATE_HEADER_COMPRESSION)
398+
if (WITH_DEFLATE_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC)
399399
list(APPEND REQUIRES_PRIVATE "zlib")
400400
endif()
401+
if (WITH_UNCOMPRESSED_CODEC)
402+
find_package(Brotli)
403+
if (Brotli_FOUND)
404+
message("Brotli found")
405+
list(APPEND REQUIRES_PRIVATE "libbrotli")
406+
else()
407+
message("Brotli not found")
408+
endif()
409+
endif()
401410

402411
list(JOIN REQUIRES_PRIVATE " " REQUIRES_PRIVATE)
403412

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,8 @@ The placeholder `{codec}` can have these values: `LIBDE265`, `X265`, `AOM_DECODE
159159

160160
Further options are:
161161

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

175175
### macOS
176176

cmake/modules/FindBrotli.cmake

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
include(FindPackageHandleStandardArgs)
2+
3+
find_path(BROTLI_DEC_INCLUDE_DIR "brotli/decode.h")
4+
find_path(BROTLI_ENC_INCLUDE_DIR "brotli/encode.h")
5+
6+
find_library(BROTLI_COMMON_LIB NAMES brotlicommon)
7+
find_library(BROTLI_DEC_LIB NAMES brotlidec)
8+
find_library(BROTLI_ENC_LIB NAMES brotlienc)
9+
10+
find_package_handle_standard_args(Brotli
11+
FOUND_VAR
12+
Brotli_FOUND
13+
REQUIRED_VARS
14+
BROTLI_COMMON_LIB
15+
BROTLI_DEC_INCLUDE_DIR
16+
BROTLI_DEC_LIB
17+
BROTLI_ENC_INCLUDE_DIR
18+
BROTLI_ENC_LIB
19+
FAIL_MESSAGE
20+
"Did not find Brotli"
21+
)
22+
23+
24+
set(HAVE_BROTLI ${Brotli_FOUND})
25+
set(BROTLI_INCLUDE_DIRS ${BROTLI_DEC_INCLUDE_DIR} ${BROTLI_ENC_INCLUDE_DIR})
26+
set(BROTLI_LIBS "${BROTLICOMMON_LIBRARY}" "${BROTLI_DEC_LIB}" "${BROTLI_ENC_LIB}")

go/heif/heif.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,13 +269,17 @@ const (
269269

270270
SuberrorCameraExtrinsicMatrixUndefined = C.heif_suberror_Camera_extrinsic_matrix_undefined
271271

272+
SuberrorDecompressionInvalidData = C.heif_suberror_Decompression_invalid_data
273+
272274
// --- Memory_allocation_error ---
273275

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

281+
CompressionInitialisationError = C.heif_suberror_Compression_initialisation_error
282+
279283
// --- Usage_error ---
280284

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

332336
SuberrorUnsupportedDataVersion = C.heif_suberror_Unsupported_data_version
333337

338+
SuberrorUnsupportedGenericCompressionMethod = C.heif_suberror_Unsupported_generic_compression_method
339+
334340
// The conversion of the source image to the requested chroma / colorspace is not supported.
335341
SuberrorUnsupportedColorConversion = C.heif_suberror_Unsupported_color_conversion
336342

libheif/CMakeLists.txt

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ set(libheif_sources
3333
init.h
3434
logging.h
3535
logging.cc
36-
metadata_compression.cc
37-
metadata_compression.h
36+
compression.h
37+
compression_brotli.cc
38+
compression_zlib.cc
3839
common_utils.cc
3940
common_utils.h
4041
region.cc
@@ -133,11 +134,21 @@ else ()
133134
message("Not compiling 'libsharpyuv'")
134135
endif ()
135136

136-
if (WITH_DEFLATE_HEADER_COMPRESSION)
137+
if (WITH_DEFLATE_HEADER_COMPRESSION OR WITH_UNCOMPRESSED_CODEC)
137138
find_package(ZLIB REQUIRED)
138139
target_link_libraries(heif PRIVATE ZLIB::ZLIB)
140+
target_compile_definitions(heif PRIVATE WITH_ZLIB_COMPRESSION=1)
141+
endif ()
142+
143+
if (WITH_DEFLATE_HEADER_COMPRESSION)
139144
target_compile_definitions(heif PRIVATE WITH_DEFLATE_HEADER_COMPRESSION=1)
140145
endif ()
146+
147+
if (HAVE_BROTLI)
148+
target_compile_definitions(heif PUBLIC HAVE_BROTLI=1)
149+
target_include_directories(heif PRIVATE ${BROTLI_INCLUDE_DIRS})
150+
target_link_libraries(heif PRIVATE ${BROTLI_LIBS})
151+
endif()
141152

142153
if (ENABLE_MULTITHREADING_SUPPORT)
143154
find_package(Threads)

libheif/api/libheif/heif.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,11 @@ enum heif_suberror_code
241241

242242
heif_suberror_No_vvcC_box = 141,
243243

244+
// icbr is only needed in some situations, this error is for those cases
245+
heif_suberror_No_icbr_box = 142,
246+
247+
// Decompressing generic compression or header compression data failed (e.g. bitstream corruption)
248+
heif_suberror_Decompression_invalid_data = 150,
244249

245250
// --- Memory_allocation_error ---
246251

@@ -249,6 +254,9 @@ enum heif_suberror_code
249254
// security limits further.
250255
heif_suberror_Security_limit_exceeded = 1000,
251256

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

253261
// --- Usage_error ---
254262

@@ -297,6 +305,8 @@ enum heif_suberror_code
297305

298306
heif_suberror_Unsupported_header_compression_method = 3005,
299307

308+
// Generically compressed data used an unsupported compression method
309+
heif_suberror_Unsupported_generic_compression_method = 3006,
300310

301311
// --- Encoder_plugin_error ---
302312

libheif/api/libheif/heif_emscripten.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,8 @@ EMSCRIPTEN_BINDINGS(libheif) {
331331
emscripten::enum_<heif_suberror_code>("heif_suberror_code")
332332
.value("heif_suberror_Unspecified", heif_suberror_Unspecified)
333333
.value("heif_suberror_Cannot_write_output_data", heif_suberror_Cannot_write_output_data)
334+
.value("heif_suberror_Compression_initialisation_error", heif_suberror_Compression_initialisation_error)
335+
.value("heif_suberror_Decompression_invalid_data", heif_suberror_Decompression_invalid_data)
334336
.value("heif_suberror_Encoder_initialization", heif_suberror_Encoder_initialization)
335337
.value("heif_suberror_Encoder_encoding", heif_suberror_Encoder_encoding)
336338
.value("heif_suberror_Encoder_cleanup", heif_suberror_Encoder_cleanup)
@@ -386,6 +388,7 @@ EMSCRIPTEN_BINDINGS(libheif) {
386388
.value("heif_suberror_Unsupported_codec", heif_suberror_Unsupported_codec)
387389
.value("heif_suberror_Unsupported_image_type", heif_suberror_Unsupported_image_type)
388390
.value("heif_suberror_Unsupported_data_version", heif_suberror_Unsupported_data_version)
391+
.value("heif_suberror_Unsupported_generic_compression_method", heif_suberror_Unsupported_generic_compression_method)
389392
.value("heif_suberror_Unsupported_color_conversion", heif_suberror_Unsupported_color_conversion)
390393
.value("heif_suberror_Unsupported_item_construction_method", heif_suberror_Unsupported_item_construction_method)
391394
.value("heif_suberror_Unsupported_header_compression_method", heif_suberror_Unsupported_header_compression_method)

libheif/bitstream.cc

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,31 @@ uint32_t BitstreamRange::read32()
235235
(buf[3]));
236236
}
237237

238+
uint64_t BitstreamRange::read64()
239+
{
240+
if (!prepare_read(8)) {
241+
return 0;
242+
}
243+
244+
uint8_t buf[8];
245+
246+
auto istr = get_istream();
247+
bool success = istr->read((char*) buf, 8);
248+
249+
if (!success) {
250+
set_eof_while_reading();
251+
return 0;
252+
}
253+
254+
return (uint64_t) (((uint64_t)buf[0] << 56) |
255+
((uint64_t)buf[1] << 48) |
256+
((uint64_t)buf[2] << 40) |
257+
((uint64_t)buf[3] << 32) |
258+
(buf[4] << 24) |
259+
(buf[5] << 16) |
260+
(buf[6] << 8) |
261+
(buf[7]));
262+
}
238263

239264
int32_t BitstreamRange::read32s()
240265
{

libheif/bitstream.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ class BitstreamRange
150150

151151
int32_t read32s();
152152

153+
uint64_t read64();
154+
153155
std::string read_string();
154156

155157
bool read(uint8_t* data, size_t n);

libheif/box.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,14 @@ Error Box::read(BitstreamRange& range, std::shared_ptr<Box>* result)
611611
case fourcc("uncC"):
612612
box = std::make_shared<Box_uncC>();
613613
break;
614+
615+
case fourcc("cmpC"):
616+
box = std::make_shared<Box_cmpC>();
617+
break;
618+
619+
case fourcc("icbr"):
620+
box = std::make_shared<Box_icbr>();
621+
break;
614622
#endif
615623

616624
// --- JPEG 2000

libheif/codecs/uncompressed_box.cc

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ std::string Box_uncC::dump(Indent& indent) const
323323
return sstr.str();
324324
}
325325

326+
326327
Error Box_uncC::write(StreamWriter& writer) const
327328
{
328329
size_t box_start = reserve_box_header_space(writer);
@@ -361,3 +362,89 @@ Error Box_uncC::write(StreamWriter& writer) const
361362

362363
return Error::Ok;
363364
}
365+
366+
367+
Error Box_cmpC::parse(BitstreamRange& range)
368+
{
369+
parse_full_box_header(range);
370+
compression_type = range.read32();
371+
uint8_t v = range.read8();
372+
must_decompress_individual_entities = ((v & 0x80) == 0x80);
373+
compressed_range_type = (v & 0x7f);
374+
return range.get_error();
375+
}
376+
377+
378+
std::string Box_cmpC::dump(Indent& indent) const
379+
{
380+
std::ostringstream sstr;
381+
sstr << Box::dump(indent);
382+
sstr << indent << "compression_type: " << to_fourcc(compression_type) << "\n";
383+
sstr << indent << "must_decompress_individual_entities: " << must_decompress_individual_entities << "\n";
384+
sstr << indent << "compressed_entity_type: " << (int)compressed_range_type << "\n";
385+
return sstr.str();
386+
}
387+
388+
Error Box_cmpC::write(StreamWriter& writer) const
389+
{
390+
size_t box_start = reserve_box_header_space(writer);
391+
392+
writer.write32(compression_type);
393+
uint8_t v = must_decompress_individual_entities ? 0x80 : 0x00;
394+
v |= (compressed_range_type & 0x7F);
395+
writer.write8(v);
396+
397+
prepend_header(writer, box_start);
398+
399+
return Error::Ok;
400+
}
401+
402+
403+
Error Box_icbr::parse(BitstreamRange& range)
404+
{
405+
parse_full_box_header(range);
406+
uint32_t num_ranges = range.read32();
407+
for (uint32_t r = 0; r < num_ranges; r++) {
408+
struct ByteRange byteRange;
409+
if (get_version() == 1) {
410+
byteRange.range_offset = range.read64();
411+
byteRange.range_size = range.read64();
412+
} else if (get_version() == 0) {
413+
byteRange.range_offset = range.read32();
414+
byteRange.range_size = range.read32();
415+
}
416+
m_ranges.push_back(byteRange);
417+
}
418+
return range.get_error();
419+
}
420+
421+
422+
std::string Box_icbr::dump(Indent& indent) const
423+
{
424+
std::ostringstream sstr;
425+
sstr << Box::dump(indent);
426+
sstr << indent << "num_ranges: " << m_ranges.size() << "\n";
427+
for (ByteRange range: m_ranges) {
428+
sstr << indent << "range_offset: " << range.range_offset << ", range_size: " << range.range_size << "\n";
429+
}
430+
return sstr.str();
431+
}
432+
433+
Error Box_icbr::write(StreamWriter& writer) const
434+
{
435+
size_t box_start = reserve_box_header_space(writer);
436+
437+
writer.write32((uint32_t)m_ranges.size());
438+
for (ByteRange range: m_ranges) {
439+
if (get_version() == 1) {
440+
writer.write64(range.range_offset);
441+
writer.write64(range.range_size);
442+
} else if (get_version() == 0) {
443+
writer.write32((uint32_t)range.range_offset);
444+
writer.write32((uint32_t)range.range_size);
445+
}
446+
}
447+
prepend_header(writer, box_start);
448+
449+
return Error::Ok;
450+
}

0 commit comments

Comments
 (0)