/* See LICENSE file for copyright and license details. */ #ifndef LIBLSS16_H #define LIBLSS16_H #include #include #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" #endif /** * Image decoding state */ enum liblss16_decode_state { /** * End of image not yet reached * * File truncated if end of file reached */ LIBLSS16_DECODE_RUNNING, /** * End of image reached * * Note that pixels may still be returned * * File holds unused data if end of file not reached */ LIBLSS16_DECODE_END_OF_IMAGE, /** * A decoding error has occurred * * Processing most be aborted */ LIBLSS16_DECODE_ERROR }; /** * Error values for image decoding */ enum liblss16_decode_error { /** * The file was at most 3 bytes long, but first * bytes where {0x3D, 0x3F, 0x41} (up to the * length of the file) */ LIBLSS16_DECODE_TRUNCATED_MAGIC, /** * The file did not start with {0x3D, 0x3F, 0x41, 0x01} * * The number of consumed bytes, include the first bad * byte in the magic. The number of consumed bytes is * 1 if the first byte was wrong. */ LIBLSS16_DECODE_BAD_MAGIC, /** * The file was too short to contain the full header */ LIBLSS16_DECODE_TRUNCATED_HEADER, /** * At least one of the follow (you can check `.width` * and `.height` to determine which apply): * * - The image width was set to 0 * * - The image height was set to 0 * * - The image height exceeded 32767 */ LIBLSS16_DECODE_BAD_IMAGE_SIZE, /** * The colour map contain a colour that had at least * once of the two high bits on the encoded byte set * * If you want to know which colour was bad (you can * only find the first one), you have to calculate it * from the number of bytes consumed before the failure * occurred. If the number of consumed bytes is `N`, * `(N - 9) / 3` is the index of the colour (the index * of the first colour is 0), and the remainder maps * to the channel: 0 = red, 1 = green, 2 = blue. */ LIBLSS16_DECODE_BAD_COLOUR, /** * Row padding was not all zeroes */ LIBLSS16_DECODE_BAD_ROW_PADDING, /** * Run-length encoding ran across two or more rows */ LIBLSS16_DECODE_EXCESSIVE_RUN_LENGTH, /** * The file was too short to contain the full image */ LIBLSS16_DECODE_TRUNCATED_IMAGE }; /** * Image encoding state */ enum liblss16_encode_state { /** * Still encoding */ LIBLSS16_ENCODE_RUNNING, /** * Encodig done */ LIBLSS16_ENCODE_DONE }; /** * Error values for image encoding */ enum liblss16_encode_error { /** * The image has zero width, zero height, * or the height exceeds 32767 */ LIBLSS16_ENCODE_BAD_IMAGE_SIZE, /** * User specified that colour were encoded in * 6 bits, however at least one colour value * had at least one of it it high bits set */ LIBLSS16_ENCODE_BAD_COLOUR }; /** * sRGB colour values, with transfer function applied, * encoded in values in [0, 255] */ struct liblss16_colour { /** * Red channel value */ uint8_t r; /** * Green channel value */ uint8_t g; /** * Blue channel value */ uint8_t b; }; /** * Image header */ struct liblss16_header { /** * The width of the raster */ uint16_t width; /** * The height of the raster */ uint16_t height; /** * Image colour map */ struct liblss16_colour colour_map[16]; }; /** * Decode state */ struct liblss16_decoder { /** * Image header */ struct liblss16_header image; /** * For internal use */ struct { uint16_t x_rem; /**< number of pixels (after `.internal.kept`) until end of row */ uint16_t y_rem; /**< number of row completions until end of image */ uint16_t kept; /**< number of duplications held for next call */ uint8_t previous; /**< previous pixel */ uint8_t saved; /**< 0 or saved nibble plus 1 */ uint8_t init_state; /**< number of header bytes read */ uint8_t state; } internal; }; /** * Encode state * * Should be considered opaque */ struct liblss16_encoder { struct liblss16_header header; uint16_t x_rem; uint16_t y_rem; uint16_t repetition; uint8_t header_position; uint8_t previous; uint8_t nbuffered; uint8_t buffered[6]; }; /** * Initialise a decoder * * @param decoder The decoder */ void liblss16_decoder_init(struct liblss16_decoder *decoder); /** * Decode an LSS16 file * * @param decoder Decoder state, must have been initialised with * `liblss16_decoder_init` before the first call * @param data Data to decode image from (may be partial) * @param length The number of bytes available in `data`; * end of file can be indicated using the value 0 * @param consumed_out The number of bytes processed from `data` * @param pixels Output buffer for the indicies of the colours * for the pixels in the image. They are returned * primarily from to the top down and secondarily * from left to right. * @param pixels_size The number of elements that may be stored in `pixels`, * must be at least 1 * @param npixels_out Output parameter for the number of elements stored * in `pixels`. Always set. * @param error_out Output parameter for the error; only set if * `LIBLSS16_DECODE_ERROR` is returned; may be `NULL` * @return The processing state: normally `LIBLSS16_DECODE_RUNNING`, * but `LIBLSS16_DECODE_END_OF_IMAGE` when the image has * been fully decoded, and `LIBLSS16_DECODE_ERROR` on error * * `decoder->width`, `decoder->height` and `decoder->colour_map` are * set once `*npixels_out` is set to a non-zero value for the first * time (or some time before that). `decoder->width` and `decoder->height` * are also set if `LIBLSS16_DECODE_ERROR` is returned and `*error_out` * is set to `LIBLSS16_DECODE_BAD_HEADER` */ 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); /** * Get error description for an error code * * @param error The error code * @return The error description */ #if defined(__GNUC__) __attribute__((__const__)) #endif const char *liblss16_decode_strerror(enum liblss16_decode_error error); /** * Initialise an encoder * * @param encoder The encoder * @param header Image information * @param rgb6 If non-zero, colour values in `header->colour_map` are * encoded in 6 bits (the limitation of the LSS16 format) * rather than 8 bits * @param error_out Output parameter for the error; only set when -1 is * returned; may be `NULL` * @return 0 on success, -1 on failure */ int liblss16_encoder_init(struct liblss16_encoder *encoder, const struct liblss16_header *header, int rgb6, enum liblss16_encode_error *error_out); /** * Encode an LSS16 file * * @param encoder Encoder state, must have been initialised with * `liblss16_encoder_init` before the first call * @param buffer Output buffer for the image data * @param size Size of `buffer` * @param written_out The number of bytes written to `size`, must be at least 1 * @param pixels Buffer with pixels to encode; the pixels are to preencoded * (before the function is called) with the colour index of * each pixel. Pixels are input primarily from to the top * down and secondarily from left to right. * @param npixels The number of provided pixels * @param nconsumed_out Output parameter for the number of pixels consumed into * to state of the encoder * @return The processing state: normally `LIBLSS16_ENCODE_RUNNING`, * but `LIBLSS16_ENCODE_DONE` when the image has been fully * encoded */ 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); /** * Get error description for an error code * * @param error The error code * @return The error description */ #if defined(__GNUC__) __attribute__((__const__)) #endif const char *liblss16_encode_strerror(enum liblss16_encode_error error); /** * Optimise and image * * @param header The image metadata * @param pixels Buffer with pixels; the pixels are to encoded with the * colour index of each pixel. Pixels are encoded primarily * from to the top down and secondarily from left to right. * * Both `pixels` and `header->colour_map` may be rewritten * (to an equivalent image with at least as good compression with LSS16) */ void liblss16_optimise(struct liblss16_header *header, uint8_t *pixels); #if defined(__clang__) # pragma clang diagnostic pop #endif #endif