/* See LICENSE file for copyright and license details. */ #include "liblss16.h" #include #include #if defined(__clang__) # pragma clang diagnostic ignored "-Wimplicit-fallthrough" # pragma clang diagnostic ignored "-Wunsafe-buffer-usage" #endif static size_t flush_buffer(struct liblss16_encoder *encoder, uint8_t *buffer, size_t size) { size_t i = 0, j = 0; for (; size && encoder->nbuffered - i > 1U; i += 2U, size--) buffer[j++] = (uint8_t)(encoder->buffered[i + 0] | (encoder->buffered[i + 1] << 4)); memmove(&encoder->buffered[0], &encoder->buffered[i], encoder->nbuffered -= (uint8_t)i); return j; } enum liblss16_encode_state liblss16_encode_from_colour_index(struct liblss16_encoder *encoder, void *buffer_, size_t size, size_t *written_out, const uint8_t *pixels, size_t npixels, size_t *nconsumed_out) { uint8_t *buffer = buffer_; size_t n; unsigned value; *written_out = 0; *nconsumed_out = 0; switch (encoder->header_position) { case 0: if (!size--) return LIBLSS16_ENCODE_RUNNING; *buffer++ = (uint8_t)0x3DU; ++*written_out; encoder->header_position++; /* fall through */ case 1: if (!size--) return LIBLSS16_ENCODE_RUNNING; *buffer++ = (uint8_t)0xF3U; ++*written_out; encoder->header_position++; /* fall through */ case 2: if (!size--) return LIBLSS16_ENCODE_RUNNING; *buffer++ = (uint8_t)0x13U; ++*written_out; encoder->header_position++; /* fall through */ case 3: if (!size--) return LIBLSS16_ENCODE_RUNNING; *buffer++ = (uint8_t)0x14U; ++*written_out; encoder->header_position++; /* fall through */ case 4: if (!size--) return LIBLSS16_ENCODE_RUNNING; *buffer++ = (uint8_t)encoder->header.width; ++*written_out; encoder->header_position++; /* fall through */ case 5: if (!size--) return LIBLSS16_ENCODE_RUNNING; *buffer++ = (uint8_t)(encoder->header.width >> 8); ++*written_out; encoder->header_position++; /* fall through */ case 6: if (!size--) return LIBLSS16_ENCODE_RUNNING; *buffer++ = (uint8_t)encoder->header.height; ++*written_out; encoder->header_position++; /* fall through */ case 7: if (!size--) return LIBLSS16_ENCODE_RUNNING; *buffer++ = (uint8_t)(encoder->header.height >> 8); ++*written_out; encoder->header_position++; /* fall through */ default: break; } while (encoder->header_position < 8U + 16U * 3U) { if (!size--) return LIBLSS16_ENCODE_RUNNING; *buffer++ = ((const uint8_t *)encoder->header.colour_map)[encoder->header_position - 8U]; ++*written_out; encoder->header_position++; } if (encoder->nbuffered > 1U) { n = flush_buffer(encoder, buffer, size); buffer = &buffer[n]; size -= n; *written_out += n; if (encoder->nbuffered > 1U) return LIBLSS16_ENCODE_RUNNING; if (!encoder->y_rem) { if (encoder->nbuffered) abort(); return LIBLSS16_ENCODE_DONE; } } if (!encoder->x_rem) goto end_of_image; while (npixels--) { if (*pixels == encoder->previous) { if (encoder->repetition == 255U + 16U) { encoder->buffered[encoder->nbuffered++] = encoder->previous; encoder->buffered[encoder->nbuffered++] = 0; encoder->buffered[encoder->nbuffered++] = (uint8_t)15U; encoder->buffered[encoder->nbuffered++] = (uint8_t)15U; n = flush_buffer(encoder, buffer, size); buffer = &buffer[n]; size -= n; *written_out += n; encoder->repetition = 0U; } encoder->repetition++; } else { if (!encoder->repetition) { /* do nothing */ } else if (encoder->repetition < 16U) { encoder->buffered[encoder->nbuffered++] = encoder->previous; encoder->buffered[encoder->nbuffered++] = (uint8_t)encoder->repetition; } else { value = encoder->repetition - 16U; encoder->buffered[encoder->nbuffered++] = encoder->previous; encoder->buffered[encoder->nbuffered++] = 0; encoder->buffered[encoder->nbuffered++] = (uint8_t)(value & 15U); encoder->buffered[encoder->nbuffered++] = (uint8_t)(value >> 4); } encoder->buffered[encoder->nbuffered++] = *pixels; encoder->previous = *pixels; encoder->repetition = 0; n = flush_buffer(encoder, buffer, size); buffer = &buffer[n]; size -= n; *written_out += n; } ++pixels; ++*nconsumed_out; --encoder->x_rem; if (encoder->nbuffered > 1U) return LIBLSS16_ENCODE_RUNNING; if (!encoder->x_rem) { end_of_image: if (!encoder->repetition) { /* do nothing */ } else if (encoder->repetition < 16U) { encoder->buffered[encoder->nbuffered++] = encoder->previous; encoder->buffered[encoder->nbuffered++] = (uint8_t)encoder->repetition; } else { value = encoder->repetition - 16U; encoder->buffered[encoder->nbuffered++] = encoder->previous; encoder->buffered[encoder->nbuffered++] = 0; encoder->buffered[encoder->nbuffered++] = (uint8_t)(value & 15U); encoder->buffered[encoder->nbuffered++] = (uint8_t)(value >> 4); } encoder->previous = 0; encoder->repetition = 0; encoder->x_rem = encoder->header.width; encoder->y_rem--; if (encoder->nbuffered & 1) encoder->buffered[encoder->nbuffered++] = 0; n = flush_buffer(encoder, buffer, size); buffer = &buffer[n]; size -= n; *written_out += n; if (encoder->nbuffered > 1U) return LIBLSS16_ENCODE_RUNNING; else if (encoder->nbuffered) abort(); if (!encoder->y_rem) return LIBLSS16_ENCODE_DONE; } } return LIBLSS16_ENCODE_RUNNING; }