Skip to content

Commit 52f7905

Browse files
committed
RleX packbits decode
1 parent 9b7930f commit 52f7905

1 file changed

Lines changed: 59 additions & 213 deletions

File tree

libGraphite/spriteworld/rleX.cpp

Lines changed: 59 additions & 213 deletions
Original file line numberDiff line numberDiff line change
@@ -135,176 +135,8 @@ auto graphite::spriteworld::rleX::surface_offset(std::int32_t frame, std::int32_
135135

136136
// MARK: - Decoding
137137

138-
static inline auto rleX_calculate_sprite_geometry(
139-
graphite::spriteworld::rleX *sprite,
140-
graphite::quickdraw::surface& surface,
141-
graphite::quickdraw::rect<std::int16_t>& rect,
142-
std::uint32_t& offset,
143-
std::uint32_t& right_offset_bound,
144-
std::uint32_t& pitch,
145-
std::uint32_t& frame
146-
) -> void {
147-
rect = sprite->frame_rect(frame);
148-
offset = (rect.origin.y * surface.size().width) + rect.origin.x;
149-
right_offset_bound = offset + rect.size.width;
150-
pitch = surface.size().width - rect.size.width;
151-
}
152-
153-
static inline auto rleX_draw_color(
154-
graphite::quickdraw::surface& surface,
155-
union graphite::quickdraw::ycbcr& color,
156-
graphite::quickdraw::rect<std::int16_t>& rect,
157-
std::uint32_t& offset,
158-
std::uint32_t& right_offset_bound,
159-
std::uint32_t& pitch,
160-
std::uint32_t count
161-
) -> void {
162-
auto rgb = graphite::quickdraw::rgb(color);
163-
for (auto i = 0; i < count; ++i) {
164-
surface.set(offset, rgb);
165-
if (++offset >= right_offset_bound) {
166-
offset += pitch;
167-
right_offset_bound = offset + rect.size.width;
168-
}
169-
}
170-
}
171-
172-
static auto rleX_opcode_handler_eof(
173-
graphite::spriteworld::rleX *sprite,
174-
graphite::quickdraw::surface& surface,
175-
graphite::data::reader& reader,
176-
graphite::quickdraw::rect<std::int16_t>& rect,
177-
std::uint32_t& offset,
178-
std::uint32_t& right_offset_bound,
179-
std::uint32_t& pitch,
180-
std::uint32_t& frame,
181-
std::uint16_t frame_count,
182-
bool& completed,
183-
union graphite::quickdraw::ycbcr& color
184-
) -> void {
185-
if (++frame >= frame_count) {
186-
completed = true;
187-
return;
188-
}
189-
rleX_calculate_sprite_geometry(sprite, surface, rect, offset, right_offset_bound, pitch, frame);
190-
}
191-
192-
static auto rleX_opcode_handler_set_luma(
193-
graphite::spriteworld::rleX *sprite,
194-
graphite::quickdraw::surface& surface,
195-
graphite::data::reader& reader,
196-
graphite::quickdraw::rect<std::int16_t>& rect,
197-
std::uint32_t& offset,
198-
std::uint32_t& right_offset_bound,
199-
std::uint32_t& pitch,
200-
std::uint32_t& frame,
201-
std::uint16_t frame_count,
202-
bool& completed,
203-
union graphite::quickdraw::ycbcr& color
204-
) -> void {
205-
color.components.y = reader.read_byte();
206-
}
207-
208-
static auto rleX_opcode_handler_set_cr(
209-
graphite::spriteworld::rleX *sprite,
210-
graphite::quickdraw::surface& surface,
211-
graphite::data::reader& reader,
212-
graphite::quickdraw::rect<std::int16_t>& rect,
213-
std::uint32_t& offset,
214-
std::uint32_t& right_offset_bound,
215-
std::uint32_t& pitch,
216-
std::uint32_t& frame,
217-
std::uint16_t frame_count,
218-
bool& completed,
219-
union graphite::quickdraw::ycbcr& color
220-
) -> void {
221-
color.components.cr = reader.read_byte();
222-
}
223-
224-
static auto rleX_opcode_handler_set_cb(
225-
graphite::spriteworld::rleX *sprite,
226-
graphite::quickdraw::surface& surface,
227-
graphite::data::reader& reader,
228-
graphite::quickdraw::rect<std::int16_t>& rect,
229-
std::uint32_t& offset,
230-
std::uint32_t& right_offset_bound,
231-
std::uint32_t& pitch,
232-
std::uint32_t& frame,
233-
std::uint16_t frame_count,
234-
bool& completed,
235-
union graphite::quickdraw::ycbcr& color
236-
) -> void {
237-
color.components.cb = reader.read_byte();
238-
}
239-
240-
static auto rleX_opcode_handler_set_alpha(
241-
graphite::spriteworld::rleX *sprite,
242-
graphite::quickdraw::surface& surface,
243-
graphite::data::reader& reader,
244-
graphite::quickdraw::rect<std::int16_t>& rect,
245-
std::uint32_t& offset,
246-
std::uint32_t& right_offset_bound,
247-
std::uint32_t& pitch,
248-
std::uint32_t& frame,
249-
std::uint16_t frame_count,
250-
bool& completed,
251-
union graphite::quickdraw::ycbcr& color
252-
) -> void {
253-
color.components.alpha = reader.read_byte();
254-
}
255-
256-
static auto rleX_opcode_handler_advance(
257-
graphite::spriteworld::rleX *sprite,
258-
graphite::quickdraw::surface& surface,
259-
graphite::data::reader& reader,
260-
graphite::quickdraw::rect<std::int16_t>& rect,
261-
std::uint32_t& offset,
262-
std::uint32_t& right_offset_bound,
263-
std::uint32_t& pitch,
264-
std::uint32_t& frame,
265-
std::uint16_t frame_count,
266-
bool& completed,
267-
union graphite::quickdraw::ycbcr& color
268-
) -> void {
269-
auto count = reader.read_long();
270-
rleX_draw_color(surface, color, rect, offset, right_offset_bound, pitch, count);
271-
}
272-
273-
static auto rleX_opcode_handler_short_advance(
274-
graphite::spriteworld::rleX *sprite,
275-
graphite::quickdraw::surface& surface,
276-
graphite::data::reader& reader,
277-
graphite::quickdraw::rect<std::int16_t>& rect,
278-
std::uint32_t& offset,
279-
std::uint32_t& right_offset_bound,
280-
std::uint32_t& pitch,
281-
std::uint32_t& frame,
282-
std::uint16_t frame_count,
283-
bool& completed,
284-
union graphite::quickdraw::ycbcr& color
285-
) -> void {
286-
auto count = reader.read_byte();
287-
rleX_draw_color(surface, color, rect, offset, right_offset_bound, pitch, static_cast<std::uint32_t>(count));
288-
}
289-
290-
typedef void(*rleX_opcode_handler)(
291-
graphite::spriteworld::rleX*,
292-
graphite::quickdraw::surface&,
293-
graphite::data::reader&,
294-
graphite::quickdraw::rect<std::int16_t>&,
295-
std::uint32_t&,
296-
std::uint32_t&,
297-
std::uint32_t&,
298-
std::uint32_t&,
299-
std::uint16_t,
300-
bool&,
301-
union graphite::quickdraw::ycbcr&
302-
);
303-
304138
auto graphite::spriteworld::rleX::decode(data::reader &reader) -> void
305139
{
306-
reader.change_byte_order(data::byte_order::lsb);
307-
308140
// Read the header of the RLE information. This will tell us what we need to do in order to actually
309141
// decode the frame.
310142
m_frame_size = quickdraw::size<std::int16_t>::read(reader, quickdraw::coding_type::macintosh);
@@ -328,51 +160,65 @@ auto graphite::spriteworld::rleX::decode(data::reader &reader) -> void
328160
// decode the RLE data correctly.
329161
m_surface = quickdraw::surface(dim * m_frame_size.width, dim * m_frame_size.height, quickdraw::colors::clear());
330162

331-
std::uint32_t current_frame = 0;
332-
std::uint32_t count = 0;
333-
std::uint32_t current_offset = 0;
163+
std::uint32_t offset = 0;
334164
std::uint32_t right_bound = 0;
335-
std::uint32_t pitch = 0;
336-
337-
bool completed_last_frame = false;
338-
auto frame = frame_rect(0);
339-
340-
union quickdraw::ycbcr yuv {
341-
.components.y = 0,
342-
.components.cb = 128,
343-
.components.cr = 128,
344-
.components.alpha = 255
345-
};
346-
347-
// NOTE: This is a very _hot_ code path and thus we need to squeeze out as much performance as possible.
348-
// Build a lookup table of opcodes, in order to reduce the number of comparisons.
349-
/* quickdraw::surface&, data::reader&, union quickdraw::ycbcr&, std::int32_t&, std::uint32_t&, quickdraw::rect<std::int16_t>& */
350-
rleX_opcode_handler opcode_lut[] = {
351-
rleX_opcode_handler_eof,
352-
rleX_opcode_handler_set_luma,
353-
rleX_opcode_handler_set_cr,
354-
rleX_opcode_handler_set_cb,
355-
rleX_opcode_handler_set_alpha,
356-
rleX_opcode_handler_advance,
357-
rleX_opcode_handler_short_advance
358-
};
359-
360-
rleX_calculate_sprite_geometry(this, m_surface, frame, current_offset, right_bound, pitch, current_frame);
361-
362-
while (!completed_last_frame) {
363-
opcode_lut[reader.read_byte()](
364-
this,
365-
m_surface,
366-
reader,
367-
frame,
368-
current_offset,
369-
right_bound,
370-
pitch,
371-
current_frame,
372-
m_frame_count,
373-
completed_last_frame,
374-
yuv
375-
);
165+
std::uint32_t pitch = m_surface.size().width - m_frame_size.width;
166+
167+
auto rect = frame_rect(0);
168+
auto raw = surface().raw().get<std::uint8_t *>();
169+
170+
171+
for (auto frame = 0; frame < m_frame_count; ++frame) {
172+
rect = this->frame_rect(frame);
173+
for (auto ch = 0; ch < 4; ++ch) {
174+
offset = (rect.origin.y * m_surface.size().width) + rect.origin.x;
175+
right_bound = offset + m_frame_size.width;
176+
auto pack_size = reader.read_long();
177+
auto pack = reader.read_data(pack_size).get<std::uint8_t *>();
178+
auto pack_offset = 0;
179+
180+
while (pack_offset < pack_size) {
181+
auto run = static_cast<std::uint32_t>(pack[pack_offset++]);
182+
if (run < 0x80) {
183+
// Copy bytes
184+
run++;
185+
if (pack_offset + run > pack_size) {
186+
throw std::runtime_error("Insufficient data for rlëX resource: " + std::to_string(m_id) + ", " + m_name);
187+
}
188+
for (auto i = 0; i < run; ++i) {
189+
raw[offset*4 + ch] = pack[pack_offset++];
190+
if (++offset >= right_bound) {
191+
offset += pitch;
192+
right_bound = offset + m_frame_size.width;
193+
}
194+
}
195+
}
196+
else {
197+
// Repeat single byte
198+
run ^= 0x80;
199+
if (run >= 0x70) {
200+
// 2 byte count
201+
if (pack_offset == pack_size) {
202+
throw std::runtime_error("Insufficient data for rlëX resource: " + std::to_string(m_id) + ", " + m_name);
203+
}
204+
// Combine 4 low bits with next byte
205+
run = (run & 0x0F) << 8 | pack[pack_offset++];
206+
}
207+
run++;
208+
if (pack_offset == pack_size) {
209+
throw std::runtime_error("Insufficient data for rlëX resource: " + std::to_string(m_id) + ", " + m_name);
210+
}
211+
auto value = pack[pack_offset++];
212+
for (auto i = 0; i < run; ++i) {
213+
raw[offset*4 + ch] = value;
214+
if (++offset >= right_bound) {
215+
offset += pitch;
216+
right_bound = offset + m_frame_size.width;
217+
}
218+
}
219+
}
220+
}
221+
}
376222
}
377223
}
378224

@@ -458,4 +304,4 @@ auto graphite::spriteworld::rleX::encode(data::writer &writer) -> void
458304

459305
writer.write_enum(opcode::eof);
460306
}
461-
}
307+
}

0 commit comments

Comments
 (0)