mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 22:17:45 +00:00
LibGfx/JBIG2: Add an initial decode_segment_header()
With `#define JBIG2_DEBUG 1` at the top of the file: % Build/lagom/bin/image --no-output \ .../JBIG2_ConformanceData-A20180829/F01_200_TT10.jb2 JBIG2LoadingContext: Organization: 0 (Sequential) JBIG2LoadingContext: Number of pages: 1 Segment number: 0 Segment type: 48 Referred to segment count: 0 Segment page association: 1 Segment data length: 19 Runtime error: JBIG2ImageDecoderPlugin: Draw the rest of the owl
This commit is contained in:
parent
5cefcad2fe
commit
177664cfae
1 changed files with 112 additions and 0 deletions
|
@ -9,12 +9,39 @@
|
|||
|
||||
// Spec: ITU-T_T_88__08_2018.pdf in the zip file here:
|
||||
// https://www.itu.int/rec/T-REC-T.88-201808-I
|
||||
// Annex H has a datastream example.
|
||||
|
||||
namespace Gfx {
|
||||
|
||||
// JBIG2 spec, Annex D, D.4.1 ID string
|
||||
static constexpr u8 id_string[] = { 0x97, 0x4A, 0x42, 0x32, 0x0D, 0x0A, 0x1A, 0x0A };
|
||||
|
||||
// 7.3 Segment types
|
||||
enum SegmentType {
|
||||
SymbolDictionary = 0,
|
||||
IntermediateTextRegion = 4,
|
||||
ImmediateTextRegion = 6,
|
||||
ImmediateLosslessTextRegion = 7,
|
||||
PatternDictionary = 16,
|
||||
IntermediateHalftoneRegion = 20,
|
||||
ImmediateHalftoneRegion = 22,
|
||||
ImmediateLosslessHalftoneRegion = 23,
|
||||
IntermediateGenericRegion = 36,
|
||||
ImmediateGenericRegion = 38,
|
||||
ImmediateLosslessGenericRegion = 39,
|
||||
IntermediateGenericRefinementRegion = 40,
|
||||
ImmediateGenericRefinementRegion = 42,
|
||||
ImmediateLosslessGenericRefinementRegion = 43,
|
||||
PageInformation = 48,
|
||||
EndOfPage = 49,
|
||||
EndOfStripe = 50,
|
||||
EndOfFile = 51,
|
||||
Profiles = 52,
|
||||
Tables = 53,
|
||||
ColorPalette = 54,
|
||||
Extension = 62,
|
||||
};
|
||||
|
||||
// Annex D
|
||||
enum class Organization {
|
||||
// D.1 Sequential organization
|
||||
|
@ -27,6 +54,14 @@ enum class Organization {
|
|||
Embedded,
|
||||
};
|
||||
|
||||
struct SegmentHeader {
|
||||
u32 segment_number;
|
||||
SegmentType type;
|
||||
Vector<u32> referred_to_segment_numbers;
|
||||
u32 page_association;
|
||||
Optional<u32> data_length;
|
||||
};
|
||||
|
||||
struct JBIG2LoadingContext {
|
||||
enum class State {
|
||||
NotDecoded = 0,
|
||||
|
@ -53,6 +88,7 @@ static ErrorOr<void> decode_jbig2_header(JBIG2LoadingContext& context)
|
|||
if (header_flags & 0b11110000)
|
||||
return Error::from_string_literal("JBIG2LoadingContext: Invalid header flags");
|
||||
context.organization = (header_flags & 1) ? Organization::Sequential : Organization::RandomAccess;
|
||||
dbgln_if(JBIG2_DEBUG, "JBIG2LoadingContext: Organization: {} ({})", (int)context.organization, context.organization == Organization::Sequential ? "Sequential" : "Random-access");
|
||||
bool has_known_number_of_pages = (header_flags & 2) ? false : true;
|
||||
bool uses_templates_with_12_AT_pixels = (header_flags & 4) ? true : false;
|
||||
bool contains_colored_region_segments = (header_flags & 8) ? true : false;
|
||||
|
@ -70,6 +106,81 @@ static ErrorOr<void> decode_jbig2_header(JBIG2LoadingContext& context)
|
|||
return {};
|
||||
}
|
||||
|
||||
static ErrorOr<SegmentHeader> decode_segment_header(JBIG2LoadingContext& context)
|
||||
{
|
||||
ReadonlyBytes data = context.data.slice(sizeof(id_string) + sizeof(u8) + (context.number_of_pages.has_value() ? sizeof(u32) : 0));
|
||||
FixedMemoryStream stream(data);
|
||||
|
||||
// 7.2.2 Segment number
|
||||
u32 segment_number = TRY(stream.read_value<BigEndian<u32>>());
|
||||
dbgln_if(JBIG2_DEBUG, "Segment number: {}", segment_number);
|
||||
|
||||
// 7.2.3 Segment header flags
|
||||
u8 flags = TRY(stream.read_value<u8>());
|
||||
SegmentType type = static_cast<SegmentType>(flags & 0b11'1111);
|
||||
dbgln_if(JBIG2_DEBUG, "Segment type: {}", (int)type);
|
||||
bool segment_page_association_size_is_32_bits = (flags & 0b100'0000) != 0;
|
||||
bool segment_retained_only_by_itself_and_extension_segments = (flags & 0b1000'00000) != 0;
|
||||
|
||||
// FIXME: Do something with these.
|
||||
(void)segment_page_association_size_is_32_bits;
|
||||
(void)segment_retained_only_by_itself_and_extension_segments;
|
||||
|
||||
// 7.2.4 Referred-to segment count and retention flags
|
||||
u8 referred_to_segment_count_and_retention_flags = TRY(stream.read_value<u8>());
|
||||
u32 count_of_referred_to_segments = referred_to_segment_count_and_retention_flags >> 5;
|
||||
if (count_of_referred_to_segments == 5 || count_of_referred_to_segments == 6)
|
||||
return Error::from_string_literal("JBIG2ImageDecoderPlugin: Invalid count_of_referred_to_segments");
|
||||
u32 extra_count = 0;
|
||||
if (count_of_referred_to_segments == 7) {
|
||||
TRY(stream.seek(-1, SeekMode::FromCurrentPosition));
|
||||
count_of_referred_to_segments = TRY(stream.read_value<BigEndian<u32>>()) & 0x1FFF'FFFF;
|
||||
extra_count = ceil_div(count_of_referred_to_segments + 1, 8);
|
||||
TRY(stream.seek(extra_count, SeekMode::FromCurrentPosition));
|
||||
}
|
||||
dbgln_if(JBIG2_DEBUG, "Referred-to segment count: {}", count_of_referred_to_segments);
|
||||
|
||||
// 7.2.5 Referred-to segment numbers
|
||||
Vector<u32> referred_to_segment_numbers;
|
||||
for (u32 i = 0; i < count_of_referred_to_segments; ++i) {
|
||||
u32 referred_to_segment_number;
|
||||
if (segment_number <= 256)
|
||||
referred_to_segment_number = TRY(stream.read_value<u8>());
|
||||
else if (segment_number <= 65536)
|
||||
referred_to_segment_number = TRY(stream.read_value<BigEndian<u16>>());
|
||||
else
|
||||
referred_to_segment_number = TRY(stream.read_value<BigEndian<u32>>());
|
||||
referred_to_segment_numbers.append(referred_to_segment_number);
|
||||
dbgln_if(JBIG2_DEBUG, "Referred-to segment number: {}", referred_to_segment_number);
|
||||
}
|
||||
|
||||
// 7.2.6 Segment page association
|
||||
u32 segment_page_association;
|
||||
if (segment_page_association_size_is_32_bits) {
|
||||
segment_page_association = TRY(stream.read_value<BigEndian<u32>>());
|
||||
} else {
|
||||
segment_page_association = TRY(stream.read_value<u8>());
|
||||
}
|
||||
dbgln_if(JBIG2_DEBUG, "Segment page association: {}", segment_page_association);
|
||||
|
||||
// 7.2.7 Segment data length
|
||||
u32 data_length = TRY(stream.read_value<BigEndian<u32>>());
|
||||
dbgln_if(JBIG2_DEBUG, "Segment data length: {}", data_length);
|
||||
|
||||
// FIXME: Add some validity checks:
|
||||
// - data_length can only be 0xffff'ffff for type ImmediateGenericRegion
|
||||
// - check type is valid
|
||||
// - check referred_to_segment_numbers are smaller than segment_number
|
||||
// - 7.3.1 Rules for segment references
|
||||
// - 7.3.2 Rules for page associations
|
||||
|
||||
Optional<u32> opt_data_length;
|
||||
if (data_length != 0xffff'ffff)
|
||||
opt_data_length = data_length;
|
||||
|
||||
return SegmentHeader { segment_number, type, move(referred_to_segment_numbers), segment_page_association, opt_data_length };
|
||||
}
|
||||
|
||||
JBIG2ImageDecoderPlugin::JBIG2ImageDecoderPlugin(ReadonlyBytes data)
|
||||
{
|
||||
m_context = make<JBIG2LoadingContext>();
|
||||
|
@ -90,6 +201,7 @@ ErrorOr<NonnullOwnPtr<ImageDecoderPlugin>> JBIG2ImageDecoderPlugin::create(Reado
|
|||
{
|
||||
auto plugin = TRY(adopt_nonnull_own_or_enomem(new (nothrow) JBIG2ImageDecoderPlugin(data)));
|
||||
TRY(decode_jbig2_header(*plugin->m_context));
|
||||
TRY(decode_segment_header(*plugin->m_context));
|
||||
return plugin;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue