Skip to content

Commit

Permalink
LibGfx/JBIG2: Implement decode_page_information()
Browse files Browse the repository at this point in the history
Also make scan_for_page_size() not early return, so that it has the
same behavior as the main decoding look. (Multiple page information
segments for a single page are likely invalid and don't happen in
practice, so this is mostly an academic change.)

Add a BitBuffer class to store the bit image data, and introduce a
Page struct for storing data associated with a page. We currently
only handle a single page, but a) this makes it easier to decode
multiple pages in the future if we want b) it makes the code easier
to understand.
  • Loading branch information
nico committed Mar 9, 2024
1 parent 1bc1cf1 commit 6929b40
Showing 1 changed file with 117 additions and 7 deletions.
124 changes: 117 additions & 7 deletions Userland/Libraries/LibGfx/ImageFormats/JBIG2Loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,85 @@ struct SegmentData {
ReadonlyBytes data;
};

class BitBuffer {
public:
static ErrorOr<NonnullOwnPtr<BitBuffer>> create(size_t width, size_t height);
bool get_bit(size_t x, size_t y) const;
void set_bit(size_t x, size_t y, bool b);
void fill(bool b);

private:
BitBuffer(Vector<u8>, size_t width, size_t height, size_t pitch);

Vector<u8> m_bits;
size_t m_width;
size_t m_height;
size_t m_pitch;
};

ErrorOr<NonnullOwnPtr<BitBuffer>> BitBuffer::create(size_t width, size_t height)
{
size_t pitch = ceil_div(width, 8ull);
Vector<u8> bits;
TRY(bits.try_resize(pitch * height));
return adopt_nonnull_own_or_enomem(new (nothrow) BitBuffer(move(bits), width, height, pitch));
}

bool BitBuffer::get_bit(size_t x, size_t y) const
{
VERIFY(x < m_width);
VERIFY(y < m_height);
size_t byte_offset = x / 8;
size_t bit_offset = x % 8;
u8 byte = m_bits[y * m_pitch + byte_offset];
byte = (byte >> (8 - 1 - bit_offset)) & 1;
return byte != 0;
}

void BitBuffer::set_bit(size_t x, size_t y, bool b)
{
VERIFY(x < m_width);
VERIFY(y < m_height);
size_t byte_offset = x / 8;
size_t bit_offset = x % 8;
u8 byte = m_bits[y * m_pitch + byte_offset];
u8 mask = 1u << (8 - 1 - bit_offset);
if (b)
byte |= mask;
else
byte &= ~mask;
m_bits[y * m_pitch + byte_offset] = byte;
}

void BitBuffer::fill(bool b)
{
u8 fill_byte = b ? 0xff : 0;
for (auto& byte : m_bits)
byte = fill_byte;
}

BitBuffer::BitBuffer(Vector<u8> bits, size_t width, size_t height, size_t pitch)
: m_bits(move(bits))
, m_width(width)
, m_height(height)
, m_pitch(pitch)
{
}

// 7.4.8.5 Page segment flags
enum class CombinationOperator {
Or = 0,
And = 1,
Xor = 2,
XNor = 3,
};

struct Page {
IntSize size;
CombinationOperator default_combination_operator;
OwnPtr<BitBuffer> bits;
};

struct JBIG2LoadingContext {
enum class State {
NotDecoded = 0,
Expand All @@ -82,7 +161,7 @@ struct JBIG2LoadingContext {
State state { State::NotDecoded };

Organization organization { Organization::Sequential };
IntSize size;
Page page;

Optional<u32> number_of_pages;

Expand Down Expand Up @@ -295,6 +374,7 @@ static ErrorOr<PageInformationSegment> decode_page_information_segment(ReadonlyB
static ErrorOr<void> scan_for_page_size(JBIG2LoadingContext& context)
{
// We only decode the first page at the moment.
bool found_size = false;
for (auto const& segment : context.segments) {
if (segment.header.type != SegmentType::PageInformation || segment.header.page_association != 1)
continue;
Expand All @@ -304,10 +384,12 @@ static ErrorOr<void> scan_for_page_size(JBIG2LoadingContext& context)
if (page_information.bitmap_height == 0xffff'ffff)
return Error::from_string_literal("JBIG2ImageDecoderPlugin: Cannot handle unknown page height yet");

context.size = { page_information.bitmap_width, page_information.bitmap_height };
return {};
context.page.size = { page_information.bitmap_width, page_information.bitmap_height };
found_size = true;
}
return Error::from_string_literal("JBIG2ImageDecoderPlugin: No page information segment found for page 1");
if (!found_size)
return Error::from_string_literal("JBIG2ImageDecoderPlugin: No page information segment found for page 1");
return {};
}

static ErrorOr<void> warn_about_multiple_pages(JBIG2LoadingContext& context)
Expand Down Expand Up @@ -412,9 +494,37 @@ static ErrorOr<void> decode_immediate_lossless_generic_refinement_region(JBIG2Lo
return Error::from_string_literal("JBIG2ImageDecoderPlugin: Cannot decode immediate lossless generic refinement region yet");
}

static ErrorOr<void> decode_page_information(JBIG2LoadingContext&, SegmentData const&)
static ErrorOr<void> decode_page_information(JBIG2LoadingContext& context, SegmentData const& segment)
{
return Error::from_string_literal("JBIG2ImageDecoderPlugin: Cannot decode page information yet");
// 7.4.8 Page information segment syntax and 8.1 Decoder model steps 1) - 3).

// "1) Decode the page information segment.""
auto page_information = TRY(decode_page_information_segment(segment.data));

bool page_is_striped = (page_information.striping_information & 0x80) != 0;
if (page_information.bitmap_height == 0xffff'ffff && !page_is_striped)
return Error::from_string_literal("JBIG2ImageDecoderPlugin: Non-striped bitmaps of indeterminate height not allowed");

u8 default_color = (page_information.flags >> 2) & 1;
u8 default_combination_operator = (page_information.flags >> 3) & 3;
context.page.default_combination_operator = static_cast<CombinationOperator>(default_combination_operator);

// FIXME: Do something with the other fields in page_information.

// "2) Create the page buffer, of the size given in the page information segment.
//
// If the page height is unknown, then this is not possible. However, in this case the page must be striped,
// and the maximum stripe height specified, and the initial page buffer can be created with height initially
// equal to this maximum stripe height."
size_t height = page_information.bitmap_height;
if (height == 0xffff'ffff)
height = page_information.striping_information & 0x7F;
context.page.bits = TRY(BitBuffer::create(page_information.bitmap_width, height));

// "3) Fill the page buffer with the page's default pixel value."
context.page.bits->fill(default_color != 0);

return {};
}

static ErrorOr<void> decode_end_of_page(JBIG2LoadingContext&, SegmentData const&)
Expand Down Expand Up @@ -599,7 +709,7 @@ JBIG2ImageDecoderPlugin::JBIG2ImageDecoderPlugin()

IntSize JBIG2ImageDecoderPlugin::size()
{
return m_context->size;
return m_context->page.size;
}

bool JBIG2ImageDecoderPlugin::sniff(ReadonlyBytes data)
Expand Down

0 comments on commit 6929b40

Please sign in to comment.