aboutsummaryrefslogtreecommitdiffstats
path: root/liblss16_decode_to_colour_index.c
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2025-03-02 18:38:02 +0100
committerMattias Andrée <m@maandree.se>2025-03-02 18:38:02 +0100
commitb60d205c290e843e05009cc709f2d3d1c1cd4aea (patch)
tree580efa2c4965d6bdd83f450d341136f7458a4db8 /liblss16_decode_to_colour_index.c
downloadliblss16-b60d205c290e843e05009cc709f2d3d1c1cd4aea.tar.gz
liblss16-b60d205c290e843e05009cc709f2d3d1c1cd4aea.tar.bz2
liblss16-b60d205c290e843e05009cc709f2d3d1c1cd4aea.tar.xz
First commit
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to 'liblss16_decode_to_colour_index.c')
-rw-r--r--liblss16_decode_to_colour_index.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/liblss16_decode_to_colour_index.c b/liblss16_decode_to_colour_index.c
new file mode 100644
index 0000000..3d184c2
--- /dev/null
+++ b/liblss16_decode_to_colour_index.c
@@ -0,0 +1,221 @@
+/* See LICENSE file for copyright and license details. */
+#include "liblss16.h"
+#include <string.h>
+
+
+enum liblss16_decode_state
+liblss16_decode_to_colour_index(struct liblss16_decoder *decoder,
+ const void *data_, size_t length, size_t *consumed_out,
+ uint8_t *pixels, size_t pixels_size, size_t *npixels_out,
+ enum liblss16_decode_error *error_out)
+{
+ const uint8_t *data = data_;
+ uint8_t n;
+ uint16_t m;
+ size_t cnt;
+
+ *consumed_out = 0;
+ *npixels_out = 0;
+
+ if (decoder->internal.kept) {
+ while (decoder->internal.kept && pixels_size) {
+ *pixels++ = decoder->internal.previous;
+ --decoder->internal.kept;
+ --pixels_size;
+ ++*npixels_out;
+ }
+ if (decoder->internal.kept)
+ return LIBLSS16_DECODE_RUNNING;
+ }
+
+ if (!length) {
+ enum liblss16_decode_error error;
+ if (decoder->internal.init_state < 4U)
+ error = LIBLSS16_DECODE_TRUNCATED_MAGIC;
+ else if (decoder->internal.init_state < 8U + 16U * 3U)
+ error = LIBLSS16_DECODE_TRUNCATED_HEADER;
+ else if (!decoder->internal.y_rem)
+ return LIBLSS16_DECODE_END_OF_IMAGE;
+ else
+ error = LIBLSS16_DECODE_TRUNCATED_IMAGE;
+ if (error_out)
+ *error_out = error;
+ return LIBLSS16_DECODE_ERROR;
+ }
+
+ switch (decoder->internal.init_state) {
+ case 0:
+ if (*data++ != 0x3DU) {
+ bad_magic:
+ if (error_out)
+ *error_out = LIBLSS16_DECODE_BAD_MAGIC;
+ return LIBLSS16_DECODE_ERROR;
+ }
+ ++*consumed_out;
+ ++decoder->internal.init_state;
+ if (!--length)
+ break;
+ /* fall through */
+
+ case 1:
+ if (*data++ != 0xF3U)
+ goto bad_magic;
+ ++*consumed_out;
+ ++decoder->internal.init_state;
+ if (!--length)
+ break;
+ /* fall through */
+
+ case 2:
+ if (*data++ != 0x13U)
+ goto bad_magic;
+ ++*consumed_out;
+ ++decoder->internal.init_state;
+ if (!--length)
+ break;
+ /* fall through */
+
+ case 3:
+ if (*data++ != 0x14U)
+ goto bad_magic;
+ ++*consumed_out;
+ ++decoder->internal.init_state;
+ if (!--length)
+ break;
+ /* fall through */
+
+ case 4:
+ decoder->width = (uint16_t)*data++;
+ ++*consumed_out;
+ ++decoder->internal.init_state;
+ if (!--length)
+ break;
+ /* fall through */
+
+ case 5:
+ decoder->width |= (uint16_t)((uint16_t)*data++ << 8);
+ ++*consumed_out;
+ ++decoder->internal.init_state;
+ if (!--length)
+ break;
+ /* fall through */
+
+ case 6:
+ decoder->height = (uint16_t)*data++;
+ ++*consumed_out;
+ ++decoder->internal.init_state;
+ if (!--length)
+ break;
+ /* fall through */
+
+ case 7:
+ decoder->height |= (uint16_t)((uint16_t)*data++ << 8);
+ ++*consumed_out;
+ ++decoder->internal.init_state;
+
+ decoder->internal.x_rem = decoder->width;
+ decoder->internal.y_rem = decoder->height;
+ if (!decoder->width || !decoder->height || decoder->height >> 15) {
+ if (error_out)
+ *error_out = LIBLSS16_DECODE_BAD_IMAGE_SIZE;
+ return LIBLSS16_DECODE_ERROR;
+ }
+
+ if (!--length)
+ break;
+ /* fall through */
+
+ default:
+ while (decoder->internal.init_state < 8U + 16U * 3U) {
+ size_t i = decoder->internal.init_state++ - 8U;
+ uint8_t value = *data++;
+ ++*consumed_out;
+ if (value & 0xC0) {
+ ((uint8_t *)decoder->colour_map)[i] = value;
+ if (error_out)
+ *error_out = LIBLSS16_DECODE_BAD_COLOUR;
+ return LIBLSS16_DECODE_ERROR;
+ } else {
+ value = (uint8_t)(((unsigned)value * 255U + 31U) / 63U);
+ ((uint8_t *)decoder->colour_map)[i] = value;
+ }
+
+ if (!--length)
+ return LIBLSS16_DECODE_RUNNING;
+ }
+
+ for (;;) {
+ if (!decoder->internal.y_rem)
+ return LIBLSS16_DECODE_END_OF_IMAGE;
+
+ if (decoder->internal.saved) {
+ n = (uint8_t)(decoder->internal.saved - 1U);
+ decoder->internal.saved = 0;
+ } else {
+ uint8_t high, low;
+ if (!pixels_size || !length)
+ return LIBLSS16_DECODE_RUNNING;
+ high = (uint8_t)(*data >> 4);
+ low = (uint8_t)(*data & 15);
+ n = low;
+ decoder->internal.saved = (uint8_t)(high + 1U);
+ ++data;
+ --length;
+ ++*consumed_out;
+ }
+
+ if (decoder->internal.state == 0U) {
+ if (n != decoder->internal.previous) {
+ decoder->internal.previous = n;
+ m = 1U;
+ goto put_m_pixels;
+ } else {
+ decoder->internal.state = 1U;
+ }
+ } else if (decoder->internal.state == 1U) {
+ if (n) {
+ m = (uint16_t)n;
+ put_m_pixels:
+ decoder->internal.state = 0U;
+ if (m > decoder->internal.x_rem) {
+ if (error_out)
+ *error_out = LIBLSS16_DECODE_EXCESSIVE_RUN_LENGTH;
+ return LIBLSS16_DECODE_ERROR;
+ }
+ decoder->internal.x_rem -= m;
+ if (!decoder->internal.x_rem) {
+ decoder->internal.x_rem = decoder->width;
+ decoder->internal.y_rem--;
+ if (decoder->internal.saved > 1U) {
+ if (error_out)
+ *error_out = LIBLSS16_DECODE_BAD_ROW_PADDING;
+ return LIBLSS16_DECODE_ERROR;
+ }
+ decoder->internal.saved = 0;
+ decoder->internal.previous = 0;
+ }
+ cnt = (size_t)m < pixels_size ? (size_t)m : pixels_size;
+ memset(pixels, decoder->internal.previous, cnt);
+ pixels = &pixels[cnt];
+ pixels_size -= cnt;
+ *npixels_out += cnt;
+ m -= (uint16_t)cnt;
+ if (m) {
+ decoder->internal.kept = m;
+ return LIBLSS16_DECODE_RUNNING;
+ }
+ } else {
+ decoder->internal.state = 2U;
+ }
+ } else if (decoder->internal.state == 2U) {
+ decoder->internal.state = (uint8_t)(n | 128U);
+ } else {
+ m = (uint16_t)((n << 4) + (decoder->internal.state & 15U) + 16U);
+ goto put_m_pixels;
+ }
+ }
+ break;
+ }
+
+ return LIBLSS16_DECODE_RUNNING;
+}