-
Notifications
You must be signed in to change notification settings - Fork 330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
more efficient QOI_RUN code #53
Conversation
The second set of changes here are really good also. I moved QOI_RUN to be 4 bit tag, 4 bit data and make QOI_GDIFF have a 2 bit tag. This results in a further 2% average increase in compression ratio and this gain helps every type of file. old: compression ratio new: compression ratio |
Implementation looks too overcomplicated for me. I think this approach is simpler: diff --git a/qoi.h b/qoi.h
index 0aec728..794e0ea 100644
--- a/qoi.h
+++ b/qoi.h
@@ -401,22 +401,17 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
run++;
}
if (
run > 0 &&
- (run == 0x2020 || px.v != px_prev.v || px_pos == px_end)
+ (px.v != px_prev.v || px_pos == px_end)
) {
- if (run < 33) {
+ do {
run -= 1;
- bytes[p++] = QOI_RUN_8 | run;
- }
- else {
- run -= 33;
- bytes[p++] = QOI_RUN_16 | run >> 8;
- bytes[p++] = run;
- }
- run = 0;
+ bytes[p++] = QOI_RUN_8 | (run & 0x1f);
+ run >>= 5;
+ } while (run);
}
if (px.v != px_prev.v) {
int index_pos = QOI_COLOR_HASH(px) % 64;
@@ -517,53 +512,55 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
}
qoi_rgba_t px = {.rgba = {.r = 0, .g = 0, .b = 0, .a = 255}};
qoi_rgba_t index[64] = {0};
- int run = 0;
+ int run = 0, run_shift = 0;
int chunks_len = size - QOI_PADDING;
for (int px_pos = 0; px_pos < px_len; px_pos += channels) {
if (run > 0) {
run--;
}
else if (p < chunks_len) {
int b1 = bytes[p++];
if ((b1 & QOI_MASK_2) == QOI_INDEX) {
px = index[b1 ^ QOI_INDEX];
+ run_shift = 0;
}
else if ((b1 & QOI_MASK_3) == QOI_RUN_8) {
- run = (b1 & 0x1f);
- }
- else if ((b1 & QOI_MASK_3) == QOI_RUN_16) {
- int b2 = bytes[p++];
- run = (((b1 & 0x1f) << 8) | (b2)) + 32;
+ run = (((b1 & 0x1f) + 1) << run_shift) - 1;
+ run_shift += 5;
}
else if ((b1 & QOI_MASK_2) == QOI_DIFF_8) {
px.rgba.r += ((b1 >> 4) & 0x03) - 2;
px.rgba.g += ((b1 >> 2) & 0x03) - 2;
px.rgba.b += ( b1 & 0x03) - 2;
+ run_shift = 0;
}
else if ((b1 & QOI_MASK_3) == QOI_DIFF_16) {
int b2 = bytes[p++];
px.rgba.r += (b1 & 0x1f) - 16;
px.rgba.g += (b2 >> 4) - 8;
px.rgba.b += (b2 & 0x0f) - 8;
+ run_shift = 0;
}
else if ((b1 & QOI_MASK_4) == QOI_DIFF_24) {
int b2 = bytes[p++];
int b3 = bytes[p++];
px.rgba.r += (((b1 & 0x0f) << 1) | (b2 >> 7)) - 16;
px.rgba.g += ((b2 & 0x7c) >> 2) - 16;
px.rgba.b += (((b2 & 0x03) << 3) | ((b3 & 0xe0) >> 5)) - 16;
px.rgba.a += (b3 & 0x1f) - 16;
+ run_shift = 0;
}
else if ((b1 & QOI_MASK_4) == QOI_COLOR) {
if (b1 & 8) { px.rgba.r = bytes[p++]; }
if (b1 & 4) { px.rgba.g = bytes[p++]; }
if (b1 & 2) { px.rgba.b = bytes[p++]; }
if (b1 & 1) { px.rgba.a = bytes[p++]; }
+ run_shift = 0;
}
index[QOI_COLOR_HASH(px) % 64] = px;
} |
Uses the approach shown in https://github.com/phoboslab/qoi/pull/41/files.
This gives 3% better compression for screenshots and 2% better for misc (no changes for photos). In addition, encoding and decoding are both sped up.