FastLED 3.9.15
Loading...
Searching...
No Matches
lzw.cpp.hpp
Go to the documentation of this file.
1/*
2 * This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
3 * Licensed under the MIT License,
4 * http://www.opensource.org/licenses/mit-license.php
5 *
6 * Copyright 2017 Michael Drake <michael.drake@codethink.co.uk>
7 * Copyright 2021 Michael Drake <tlsa@netsurf-browser.org>
8 */
9
10#include "fl/stl/cstddef.h"
11#include "fl/stl/assert.h"
12#include "fl/stl/int.h"
13#include "fl/stl/allocator.h"
14#include "fl/stl/noexcept.h"
15
16#include "lzw.h"
17
18namespace fl {
19namespace third_party {
20
27
29#define LZW_TABLE_ENTRY_MAX (1u << LZW_CODE_MAX)
30
43 const fl::u8 * data;
44 fl::size data_len;
45 fl::size data_sb_next;
46
47 const fl::u8 *sb_data;
48 fl::size sb_bit;
49 fl::u32 sb_bit_count;
50};
51
68
102
103/* Exported function, documented in lzw.h */
105{
106 struct lzw_ctx *c = static_cast<struct lzw_ctx*>(fl::Malloc(sizeof(*c)));
107 if (c == nullptr) {
108 return LZW_NO_MEM;
109 }
110
111 *ctx = c;
112 return LZW_OK;
113}
114
115/* Exported function, documented in lzw.h */
117{
118 fl::Free(ctx);
119}
120
128{
129 fl::size block_size;
130 fl::size next_block_pos = ctx->data_sb_next;
131 const fl::u8 *data_next = ctx->data + next_block_pos;
132
133 if (next_block_pos >= ctx->data_len) {
134 return LZW_NO_DATA;
135 }
136
137 block_size = *data_next;
138
139 if ((next_block_pos + block_size) >= ctx->data_len) {
140 return LZW_NO_DATA;
141 }
142
143 ctx->sb_bit = 0;
144 ctx->sb_bit_count = block_size * 8;
145
146 if (block_size == 0) {
147 ctx->data_sb_next += 1;
148 return LZW_OK_EOD;
149 }
150
151 ctx->sb_data = data_next + 1;
152 ctx->data_sb_next += block_size + 1;
153
154 return LZW_OK;
155}
156
168 struct lzw_read_ctx * ctx,
169 fl::u16 code_size,
170 fl::u16 * code_out) FL_NOEXCEPT
171{
172 fl::u32 code = 0;
173 fl::u32 current_bit = ctx->sb_bit & 0x7;
174
175 if (ctx->sb_bit + 24 <= ctx->sb_bit_count) {
176 /* Fast path: read three bytes from this sub-block */
177 const fl::u8 *data = ctx->sb_data + (ctx->sb_bit >> 3);
178 code |= static_cast<fl::u32>(*data++) << 0;
179 code |= static_cast<fl::u32>(*data++) << 8;
180 code |= static_cast<fl::u32>(*data) << 16;
181 ctx->sb_bit += code_size;
182 } else {
183 /* Slow path: code spans sub-blocks */
184 fl::u8 byte_advance = (current_bit + code_size) >> 3;
185 fl::u8 byte = 0;
186 fl::u8 bits_remaining_0 = (code_size < (8u - current_bit)) ?
187 code_size : (8u - current_bit);
188 fl::u8 bits_remaining_1 = code_size - bits_remaining_0;
189 fl::u8 bits_used[3] = {
190 bits_remaining_0,
191 (fl::u8)(bits_remaining_1 < 8 ? bits_remaining_1 : 8),
192 (fl::u8)(bits_remaining_1 - 8),
193 };
194
195 FL_ASSERT(byte_advance <= 2, "Byte advance too large");
196
197 while (true) {
198 const fl::u8 *data = ctx->sb_data;
199 lzw_result res;
200
201 /* Get any data from end of this sub-block */
202 while (byte <= byte_advance &&
203 ctx->sb_bit < ctx->sb_bit_count) {
204 code |= data[ctx->sb_bit >> 3] << (byte << 3);
205 ctx->sb_bit += bits_used[byte];
206 byte++;
207 }
208
209 /* Check if we have all we need */
210 if (byte > byte_advance) {
211 break;
212 }
213
214 /* Move to next sub-block */
215 res = lzw__block_advance(ctx);
216 if (res != LZW_OK) {
217 return res;
218 }
219 }
220 }
221
222 *code_out = (code >> current_bit) & ((1 << code_size) - 1);
223 return LZW_OK;
224}
225
234 struct lzw_ctx *ctx,
235 fl::u16 *code_out) FL_NOEXCEPT
236{
237 fl::u16 code = 0;
238
239 /* Reset table building context */
240 ctx->code_size = ctx->initial_code_size;
241 ctx->code_max = (1 << ctx->initial_code_size) - 1;
242 ctx->table_size = ctx->eoi_code + 1;
243
244 /* There might be a sequence of clear codes, so process them all */
245 do {
246 lzw_result res = lzw__read_code(&ctx->input,
247 ctx->code_size, &code);
248 if (res != LZW_OK) {
249 return res;
250 }
251 } while (code == ctx->clear_code);
252
253 /* The initial code must be from the initial table. */
254 if (code > ctx->clear_code) {
255 return LZW_BAD_ICODE;
256 }
257
258 *code_out = code;
259 return LZW_OK;
260}
261
262/* Exported function, documented in lzw.h */
264 struct lzw_ctx *ctx,
265 fl::u8 minimum_code_size,
266 const fl::u8 *input_data,
267 fl::size input_length,
268 fl::size input_pos) FL_NOEXCEPT
269{
270 struct lzw_table_entry *table = ctx->table;
271 lzw_result res;
272 fl::u16 code;
273
274 if (minimum_code_size >= LZW_CODE_MAX) {
275 return LZW_BAD_ICODE;
276 }
277
278 /* Initialise the input reading context */
279 ctx->input.data = input_data;
280 ctx->input.data_len = input_length;
281 ctx->input.data_sb_next = input_pos;
282
283 ctx->input.sb_bit = 0;
284 ctx->input.sb_bit_count = 0;
285
286 /* Initialise the table building context */
287 ctx->initial_code_size = minimum_code_size + 1;
288
289 ctx->clear_code = (1 << minimum_code_size) + 0;
290 ctx->eoi_code = (1 << minimum_code_size) + 1;
291
292 ctx->output_left = 0;
293
294 /* Initialise the standard table entries */
295 for (fl::u16 i = 0; i < ctx->clear_code; i++) {
296 table[i].first = i;
297 table[i].value = i;
298 table[i].count = 1;
299 }
300
301 res = lzw__handle_clear(ctx, &code);
302 if (res != LZW_OK) {
303 return res;
304 }
305
306 /* Store details of this code as "previous code" to the context. */
307 ctx->prev_code_first = ctx->table[code].first;
308 ctx->prev_code_count = ctx->table[code].count;
309 ctx->prev_code = code;
310
311 /* Add code to context for immediate output. */
312 ctx->output_code = code;
313 ctx->output_left = 1;
314
315 ctx->has_transparency = false;
316 ctx->transparency_idx = 0;
317 ctx->colour_map = nullptr;
318
319 return LZW_OK;
320}
321
322/* Exported function, documented in lzw.h */
324 struct lzw_ctx *ctx,
325 fl::u8 minimum_code_size,
326 fl::u32 transparency_idx,
327 const fl::u32 *colour_table,
328 const fl::u8 *input_data,
329 fl::size input_length,
330 fl::size input_pos) FL_NOEXCEPT
331{
332 lzw_result res;
333
334 if (colour_table == nullptr) {
335 return LZW_BAD_PARAM;
336 }
337
338 res = lzw_decode_init(ctx, minimum_code_size,
339 input_data, input_length, input_pos);
340 if (res != LZW_OK) {
341 return res;
342 }
343
344 ctx->has_transparency = (transparency_idx <= 0xFF);
345 ctx->transparency_idx = transparency_idx;
346 ctx->colour_map = colour_table;
347
348 return LZW_OK;
349}
350
357static inline void lzw__table_add_entry(
358 struct lzw_ctx *ctx,
359 fl::u16 code) FL_NOEXCEPT
360{
361 struct lzw_table_entry *entry = &ctx->table[ctx->table_size];
362
363 entry->value = code;
364 entry->first = ctx->prev_code_first;
365 entry->count = ctx->prev_code_count + 1;
366 entry->extends = ctx->prev_code;
367
368 ctx->table_size++;
369}
370
371typedef fl::u32 (*lzw_writer_fn)(
372 struct lzw_ctx *ctx,
373 void * output_data,
374 fl::u32 output_length,
375 fl::u32 output_pos,
376 fl::u16 code,
377 fl::u16 left);
378
390 struct lzw_ctx *ctx,
391 lzw_writer_fn write_fn,
392 void * output_data,
393 fl::u32 output_length,
394 fl::u32 * output_written) FL_NOEXCEPT
395{
396 lzw_result res;
397 fl::u16 code = 0;
398
399 /* Get a new code from the input */
400 res = lzw__read_code(&ctx->input, ctx->code_size, &code);
401 if (res != LZW_OK) {
402 return res;
403 }
404
405 /* Handle the new code */
406 if (code == ctx->eoi_code) {
407 /* Got End of Information code */
408 return LZW_EOI_CODE;
409
410 } else if (code > ctx->table_size) {
411 /* Code is invalid */
412 return LZW_BAD_CODE;
413
414 } else if (code == ctx->clear_code) {
415 res = lzw__handle_clear(ctx, &code);
416 if (res != LZW_OK) {
417 return res;
418 }
419
420 } else if (ctx->table_size < LZW_TABLE_ENTRY_MAX) {
421 fl::u16 size = ctx->table_size;
422 lzw__table_add_entry(ctx, (code < size) ?
423 ctx->table[code].first :
424 ctx->prev_code_first);
425
426 /* Ensure code size is increased, if needed. */
427 if (size == ctx->code_max && ctx->code_size < LZW_CODE_MAX) {
428 ctx->code_size++;
429 ctx->code_max = (1 << ctx->code_size) - 1;
430 }
431 }
432
433 *output_written += write_fn(ctx,
434 output_data, output_length, *output_written,
435 code, ctx->table[code].count);
436
437 /* Store details of this code as "previous code" to the context. */
438 ctx->prev_code_first = ctx->table[code].first;
439 ctx->prev_code_count = ctx->table[code].count;
440 ctx->prev_code = code;
441
442 return LZW_OK;
443}
444
461static inline fl::u32 lzw__write_fn(struct lzw_ctx *ctx,
462 void * output_data,
463 fl::u32 output_length,
464 fl::u32 output_used,
465 fl::u16 code,
466 fl::u16 left) FL_NOEXCEPT
467{
468 fl::u8 * output_pos = (fl::u8 *)output_data + output_used;
469 const struct lzw_table_entry * const table = ctx->table;
470 fl::u32 space = output_length - output_used;
471 fl::u16 count = left;
472
473 if (count > space) {
474 left = count - space;
475 count = space;
476 } else {
477 left = 0;
478 }
479
480 ctx->output_code = code;
481 ctx->output_left = left;
482
483 /* Skip over any values we don't have space for. */
484 for (unsigned i = left; i != 0; i--) {
485 const struct lzw_table_entry *entry = table + code;
486 code = entry->extends;
487 }
488
489 output_pos += count;
490 for (unsigned i = count; i != 0; i--) {
491 const struct lzw_table_entry *entry = table + code;
492 *--output_pos = entry->value;
493 code = entry->extends;
494 }
495
496 return count;
497}
498
499/* Exported function, documented in lzw.h */
501 const fl::u8 * *const output_data,
502 fl::u32 * output_written) FL_NOEXCEPT
503{
504 const fl::u32 output_length = sizeof(ctx->stack_base);
505
506 *output_written = 0;
507 *output_data = ctx->stack_base;
508
509 if (ctx->output_left != 0) {
510 *output_written += lzw__write_fn(ctx,
511 ctx->stack_base, output_length, *output_written,
512 ctx->output_code, ctx->output_left);
513 }
514
515 while (*output_written != output_length) {
517 ctx->stack_base, output_length, output_written);
518 if (res != LZW_OK) {
519 return res;
520 }
521 }
522
523 return LZW_OK;
524}
525
542static inline fl::u32 lzw__map_write_fn(struct lzw_ctx *ctx,
543 void * output_data,
544 fl::u32 output_length,
545 fl::u32 output_used,
546 fl::u16 code,
547 fl::u16 left) FL_NOEXCEPT
548{
549 fl::u32 * output_pos = (fl::u32 *)output_data + output_used;
550 const struct lzw_table_entry * const table = ctx->table;
551 fl::u32 space = output_length - output_used;
552 fl::u16 count = left;
553
554 if (count > space) {
555 left = count - space;
556 count = space;
557 } else {
558 left = 0;
559 }
560
561 ctx->output_code = code;
562 ctx->output_left = left;
563
564 for (unsigned i = left; i != 0; i--) {
565 const struct lzw_table_entry *entry = table + code;
566 code = entry->extends;
567 }
568
569 output_pos += count;
570 if (ctx->has_transparency) {
571 for (unsigned i = count; i != 0; i--) {
572 const struct lzw_table_entry *entry = table + code;
573 --output_pos;
574 if (entry->value != ctx->transparency_idx) {
575 *output_pos = ctx->colour_map[entry->value];
576 }
577 code = entry->extends;
578 }
579 } else {
580 for (unsigned i = count; i != 0; i--) {
581 const struct lzw_table_entry *entry = table + code;
582 *--output_pos = ctx->colour_map[entry->value];
583 code = entry->extends;
584 }
585 }
586
587 return count;
588}
589
590/* Exported function, documented in lzw.h */
592 fl::u32 * output_data,
593 fl::u32 output_length,
594 fl::u32 * output_written) FL_NOEXCEPT
595{
596 *output_written = 0;
597
598 if (ctx->colour_map == nullptr) {
599 return LZW_NO_COLOUR;
600 }
601
602 if (ctx->output_left != 0) {
603 *output_written += lzw__map_write_fn(ctx,
604 output_data, output_length, *output_written,
605 ctx->output_code, ctx->output_left);
606 }
607
608 while (*output_written != output_length) {
610 output_data, output_length, output_written);
611 if (res != LZW_OK) {
612 return res;
613 }
614 }
615
616 return LZW_OK;
617}
618
619} // namespace third_party
620} // namespace fl
#define FL_ASSERT(x, MSG)
Definition assert.h:6
#define LZW_TABLE_ENTRY_MAX
Maximum number of lzw table entries.
Definition lzw.cpp.hpp:29
#define LZW_CODE_MAX
Maximum LZW code size in bits.
Definition lzw.h:27
LZW decompression (interface)
uint8_t byte
Definition midi_Defs.h:36
unsigned char u8
Definition coder.h:132
fl::u32(* lzw_writer_fn)(struct lzw_ctx *ctx, void *output_data, fl::u32 output_length, fl::u32 output_pos, fl::u16 code, fl::u16 left)
Definition lzw.cpp.hpp:371
lzw_result lzw_decode_init(struct lzw_ctx *ctx, fl::u8 minimum_code_size, const fl::u8 *input_data, fl::size input_length, fl::size input_pos) FL_NOEXCEPT
Initialise an LZW decompression context for decoding.
Definition lzw.cpp.hpp:263
lzw_result lzw_context_create(struct lzw_ctx **ctx) FL_NOEXCEPT
Create an LZW decompression context.
Definition lzw.cpp.hpp:104
void lzw_context_destroy(struct lzw_ctx *ctx) FL_NOEXCEPT
Destroy an LZW decompression context.
Definition lzw.cpp.hpp:116
static lzw_result lzw__decode(struct lzw_ctx *ctx, lzw_writer_fn write_fn, void *output_data, fl::u32 output_length, fl::u32 *output_written) FL_NOEXCEPT
Get the next LZW code and write its value(s) to output buffer.
Definition lzw.cpp.hpp:389
static fl::u32 lzw__map_write_fn(struct lzw_ctx *ctx, void *output_data, fl::u32 output_length, fl::u32 output_used, fl::u16 code, fl::u16 left) FL_NOEXCEPT
Write colour mapped values for this code to the output.
Definition lzw.cpp.hpp:542
static lzw_result lzw__read_code(struct lzw_read_ctx *ctx, fl::u16 code_size, fl::u16 *code_out) FL_NOEXCEPT
Get the next LZW code of given size from the raw input data.
Definition lzw.cpp.hpp:167
lzw_result
LZW decoding response codes.
Definition lzw.h:33
@ LZW_NO_DATA
Error: Out of data.
Definition lzw.h:37
@ LZW_EOI_CODE
Error: End of Information code.
Definition lzw.h:38
@ LZW_OK
Success.
Definition lzw.h:34
@ LZW_NO_COLOUR
Error: No colour map provided.
Definition lzw.h:39
@ LZW_BAD_CODE
Error: Bad LZW code.
Definition lzw.h:42
@ LZW_NO_MEM
Error: Out of memory.
Definition lzw.h:36
@ LZW_BAD_PARAM
Error: Bad function parameter.
Definition lzw.h:41
@ LZW_OK_EOD
Success; reached zero-length sub-block.
Definition lzw.h:35
@ LZW_BAD_ICODE
Error: Bad initial LZW code.
Definition lzw.h:40
static fl::u32 lzw__write_fn(struct lzw_ctx *ctx, void *output_data, fl::u32 output_length, fl::u32 output_used, fl::u16 code, fl::u16 left) FL_NOEXCEPT
Write values for this code to the output stack.
Definition lzw.cpp.hpp:461
lzw_result lzw_decode_init_map(struct lzw_ctx *ctx, fl::u8 minimum_code_size, fl::u32 transparency_idx, const fl::u32 *colour_table, const fl::u8 *input_data, fl::size input_length, fl::size input_pos) FL_NOEXCEPT
Initialise an LZW decompression context for decoding to colour map values.
Definition lzw.cpp.hpp:323
static void lzw__table_add_entry(struct lzw_ctx *ctx, fl::u16 code) FL_NOEXCEPT
Create new table entry.
Definition lzw.cpp.hpp:357
lzw_result lzw_decode(struct lzw_ctx *ctx, const fl::u8 **const output_data, fl::u32 *output_written) FL_NOEXCEPT
Read input codes until end of LZW context owned output buffer.
Definition lzw.cpp.hpp:500
static lzw_result lzw__handle_clear(struct lzw_ctx *ctx, fl::u16 *code_out) FL_NOEXCEPT
Handle clear code.
Definition lzw.cpp.hpp:233
static lzw_result lzw__block_advance(struct lzw_read_ctx *ctx) FL_NOEXCEPT
Advance the context to the next sub-block in the input data.
Definition lzw.cpp.hpp:127
lzw_result lzw_decode_map(struct lzw_ctx *ctx, fl::u32 *output_data, fl::u32 output_length, fl::u32 *output_written) FL_NOEXCEPT
Read LZW codes into client buffer, mapping output to colours.
Definition lzw.cpp.hpp:591
fl::u8 first
First value in entry's entire record.
Definition lzw.cpp.hpp:64
fl::u16 extends
Offset in table to previous entry.
Definition lzw.cpp.hpp:66
fl::u16 table_size
Next position in table to fill.
Definition lzw.cpp.hpp:87
fl::size data_sb_next
Offset to sub-block size.
Definition lzw.cpp.hpp:45
fl::u16 clear_code
Special Clear code value.
Definition lzw.cpp.hpp:84
const fl::u8 * sb_data
Pointer to current sub-block in data.
Definition lzw.cpp.hpp:47
bool has_transparency
Whether the image is opaque.
Definition lzw.cpp.hpp:92
fl::u32 sb_bit_count
Bit count in sub-block.
Definition lzw.cpp.hpp:49
fl::u16 prev_code_first
First value of previous code.
Definition lzw.cpp.hpp:76
struct lzw_table_entry table[LZW_TABLE_ENTRY_MAX]
LZW code table.
Definition lzw.cpp.hpp:97
fl::u16 eoi_code
Special End of Information code value.
Definition lzw.cpp.hpp:85
fl::size sb_bit
Current bit offset in sub-block.
Definition lzw.cpp.hpp:48
fl::u16 count
Count of values in this entry's record.
Definition lzw.cpp.hpp:65
const fl::u32 * colour_map
Index to colour mapping.
Definition lzw.cpp.hpp:94
fl::u8 initial_code_size
Starting LZW code size.
Definition lzw.cpp.hpp:79
fl::u8 transparency_idx
Index representing transparency.
Definition lzw.cpp.hpp:93
fl::u8 code_size
Current LZW code size.
Definition lzw.cpp.hpp:81
fl::size data_len
Input data length.
Definition lzw.cpp.hpp:44
fl::u16 prev_code
Code read from input previously.
Definition lzw.cpp.hpp:75
struct lzw_read_ctx input
Input reading context.
Definition lzw.cpp.hpp:73
fl::u8 value
Last value for record ending at entry.
Definition lzw.cpp.hpp:63
fl::u16 output_left
Number of values left for output_code.
Definition lzw.cpp.hpp:90
const fl::u8 * data
Pointer to start of input data.
Definition lzw.cpp.hpp:43
fl::u16 code_max
Max code value for current code size.
Definition lzw.cpp.hpp:82
fl::u16 output_code
Code that has been partially output.
Definition lzw.cpp.hpp:89
fl::u8 stack_base[LZW_TABLE_ENTRY_MAX]
Output value stack.
Definition lzw.cpp.hpp:100
fl::u16 prev_code_count
Total values for previous code.
Definition lzw.cpp.hpp:77
LZW decompression context.
Definition lzw.cpp.hpp:72
LZW table entry.
Definition lzw.cpp.hpp:62
Context for reading LZW data.
Definition lzw.cpp.hpp:42
void Free(void *ptr)
void * Malloc(fl::size size)
Base definition for an LED controller.
Definition crgb.hpp:179
#define FL_NOEXCEPT