mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 07:30:19 +00:00
LibGfx/JBIG2: Implement decode_page_information()
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.
This commit is contained in:
parent
4b01f2f158
commit
54982857bd
Notes:
sideshowbarker
2024-07-16 21:34:08 +09:00
Author: https://github.com/nico Commit: https://github.com/SerenityOS/serenity/commit/54982857bd Pull-request: https://github.com/SerenityOS/serenity/pull/23534 Reviewed-by: https://github.com/LucasChollet
1 changed files with 117 additions and 7 deletions
|
@ -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,
|
||||
|
@ -82,7 +161,7 @@ struct JBIG2LoadingContext {
|
|||
State state { State::NotDecoded };
|
||||
|
||||
Organization organization { Organization::Sequential };
|
||||
IntSize size;
|
||||
Page page;
|
||||
|
||||
Optional<u32> number_of_pages;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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&)
|
||||
|
@ -599,7 +709,7 @@ JBIG2ImageDecoderPlugin::JBIG2ImageDecoderPlugin()
|
|||
|
||||
IntSize JBIG2ImageDecoderPlugin::size()
|
||||
{
|
||||
return m_context->size;
|
||||
return m_context->page.size;
|
||||
}
|
||||
|
||||
bool JBIG2ImageDecoderPlugin::sniff(ReadonlyBytes data)
|
||||
|
|
Loading…
Reference in a new issue