Change header to big endian; make it independent from host byte order; close #10
This commit is contained in:
parent
c03edb2f26
commit
81b438cb56
80
qoi.h
80
qoi.h
@ -72,13 +72,13 @@ you can define QOI_MALLOC and QOI_FREE before including this library.
|
|||||||
|
|
||||||
-- Data Format
|
-- Data Format
|
||||||
|
|
||||||
A QOI file has the following header, followed by any number of data "chunks".
|
A QOI file has a 12 byte header, followed by any number of data "chunks".
|
||||||
|
|
||||||
struct qoi_header_t {
|
struct qoi_header_t {
|
||||||
char [4]; // magic bytes "qoif"
|
char [4]; // magic bytes "qoif"
|
||||||
unsigned short width; // image width in pixels
|
unsigned short width; // image width in pixels (BE)
|
||||||
unsigned short height; // image height in pixels
|
unsigned short height; // image height in pixels (BE)
|
||||||
unsigned int size; // number of data bytes following this header
|
unsigned int size; // number of data bytes following this header (BE)
|
||||||
};
|
};
|
||||||
|
|
||||||
The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous
|
The decoder and encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous
|
||||||
@ -235,27 +235,22 @@ void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channel
|
|||||||
#define QOI_MASK_4 0xf0 // 11110000
|
#define QOI_MASK_4 0xf0 // 11110000
|
||||||
|
|
||||||
#define QOI_COLOR_HASH(C) (C.rgba.r ^ C.rgba.g ^ C.rgba.b ^ C.rgba.a)
|
#define QOI_COLOR_HASH(C) (C.rgba.r ^ C.rgba.g ^ C.rgba.b ^ C.rgba.a)
|
||||||
|
#define QOI_MAGIC \
|
||||||
|
(((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
|
||||||
|
((unsigned int)'i') << 8 | ((unsigned int)'f'))
|
||||||
|
#define QOI_HEADER_SIZE 12
|
||||||
#define QOI_PADDING 4
|
#define QOI_PADDING 4
|
||||||
|
|
||||||
|
#define QOI_READ_16(B, P) ((B[P++] & 0xff) << 8 | (B[P++] & 0xff))
|
||||||
|
#define QOI_READ_32(B, P) (QOI_READ_16(B, P) << 16 | QOI_READ_16(B, P))
|
||||||
|
#define QOI_WRITE_16(B, P, V) (B[P++] = (0xff00 & V) >> 8, B[P++] = (0xff & V))
|
||||||
|
#define QOI_WRITE_32(B, P, V) (QOI_WRITE_16(B, P, V >> 16), QOI_WRITE_16(B, P, V))
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct { unsigned char r, g, b, a; } rgba;
|
struct { unsigned char r, g, b, a; } rgba;
|
||||||
unsigned int v;
|
unsigned int v;
|
||||||
} qoi_rgba_t;
|
} qoi_rgba_t;
|
||||||
|
|
||||||
typedef union {
|
|
||||||
char chars[4];
|
|
||||||
unsigned int v;
|
|
||||||
} qoi_magic_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
qoi_magic_t magic;
|
|
||||||
unsigned short width;
|
|
||||||
unsigned short height;
|
|
||||||
unsigned int size;
|
|
||||||
} qoi_header_t;
|
|
||||||
|
|
||||||
const static qoi_magic_t qoi_magic = {.chars = {'q','o','i','f'}};
|
|
||||||
|
|
||||||
void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) {
|
void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) {
|
||||||
if (
|
if (
|
||||||
data == NULL || out_len == NULL ||
|
data == NULL || out_len == NULL ||
|
||||||
@ -266,20 +261,17 @@ void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int max_size = w * h * (channels + 1) + sizeof(qoi_header_t) + 4;
|
int max_size = w * h * (channels + 1) + QOI_HEADER_SIZE + QOI_PADDING;
|
||||||
int p = 0;
|
int p = 0;
|
||||||
unsigned char *bytes = QOI_MALLOC(max_size);
|
unsigned char *bytes = QOI_MALLOC(max_size);
|
||||||
if (!bytes) {
|
if (!bytes) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*(qoi_header_t *)bytes = (qoi_header_t) {
|
QOI_WRITE_32(bytes, p, QOI_MAGIC);
|
||||||
.magic = qoi_magic,
|
QOI_WRITE_16(bytes, p, w);
|
||||||
.width = w,
|
QOI_WRITE_16(bytes, p, h);
|
||||||
.height = h,
|
QOI_WRITE_32(bytes, p, 0); // size, will be set later
|
||||||
.size = 0 // will be set at the end
|
|
||||||
};
|
|
||||||
p += sizeof(qoi_header_t);
|
|
||||||
|
|
||||||
const unsigned char *pixels = (const unsigned char *)data;
|
const unsigned char *pixels = (const unsigned char *)data;
|
||||||
|
|
||||||
@ -371,44 +363,50 @@ void *qoi_encode(const void *data, int w, int h, int channels, int *out_len) {
|
|||||||
bytes[p++] = 0;
|
bytes[p++] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
((qoi_header_t *)bytes)->size = p - sizeof(qoi_header_t);
|
int data_len = p - QOI_HEADER_SIZE;
|
||||||
*out_len = p;
|
*out_len = p;
|
||||||
|
|
||||||
|
p = 8;
|
||||||
|
QOI_WRITE_32(bytes, p, data_len);
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channels) {
|
void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channels) {
|
||||||
if (size < sizeof(qoi_header_t)) {
|
if (channels < 3 || channels > 4 || size < QOI_HEADER_SIZE) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
qoi_header_t *header = (qoi_header_t *)data;
|
const unsigned char *bytes = (const unsigned char *)data;
|
||||||
|
int p = 0;
|
||||||
|
|
||||||
|
int magic = QOI_READ_32(bytes, p);
|
||||||
|
int w = QOI_READ_16(bytes, p);
|
||||||
|
int h = QOI_READ_16(bytes, p);
|
||||||
|
int data_len = QOI_READ_32(bytes, p);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
channels < 3 || channels > 4 ||
|
w == 0 || h == 0 || magic != QOI_MAGIC ||
|
||||||
!header->width || !header->height ||
|
size != data_len + QOI_HEADER_SIZE
|
||||||
header->size + sizeof(qoi_header_t) != size ||
|
|
||||||
header->magic.v != qoi_magic.v
|
|
||||||
) {
|
) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int px_len = header->width * header->height * channels;
|
int px_len = w * h * channels;
|
||||||
unsigned char *pixels = QOI_MALLOC(px_len);
|
unsigned char *pixels = QOI_MALLOC(px_len);
|
||||||
if (!pixels) {
|
if (!pixels) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const unsigned char *bytes = (const unsigned char *)data + sizeof(qoi_header_t);
|
|
||||||
|
|
||||||
int data_len = header->size - QOI_PADDING;
|
|
||||||
qoi_rgba_t px = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}};
|
qoi_rgba_t px = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}};
|
||||||
qoi_rgba_t index[64] = {0};
|
qoi_rgba_t index[64] = {0};
|
||||||
|
|
||||||
int run = 0;
|
int run = 0;
|
||||||
for (int px_pos = 0, p = 0; px_pos < px_len; px_pos += channels) {
|
int chunks_len = size - QOI_PADDING;
|
||||||
|
for (int px_pos = 0; px_pos < px_len; px_pos += channels) {
|
||||||
if (run > 0) {
|
if (run > 0) {
|
||||||
run--;
|
run--;
|
||||||
}
|
}
|
||||||
else if (p < data_len) {
|
else if (p < chunks_len) {
|
||||||
int b1 = bytes[p++];
|
int b1 = bytes[p++];
|
||||||
|
|
||||||
if ((b1 & QOI_MASK_2) == QOI_INDEX) {
|
if ((b1 & QOI_MASK_2) == QOI_INDEX) {
|
||||||
@ -460,8 +458,8 @@ void *qoi_decode(const void *data, int size, int *out_w, int *out_h, int channel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*out_w = header->width;
|
*out_w = w;
|
||||||
*out_h = header->height;
|
*out_h = h;
|
||||||
return pixels;
|
return pixels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user