aboutsummaryrefslogtreecommitdiffstats
path: root/liblss16.h
blob: 3085ec94b553695a659420f96e4330b89b1c83f7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
/* See LICENSE file for copyright and license details. */
#ifndef LIBLSS16_H
#define LIBLSS16_H

#include <stddef.h>
#include <stdint.h>


#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