mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Cleanup QOI
This commit is contained in:
@@ -73,18 +73,19 @@ import std::io;
|
||||
*>
|
||||
fn usz! write(String filename, char[] input, QOIDesc* desc)
|
||||
{
|
||||
@pool() {
|
||||
@pool()
|
||||
{
|
||||
// encode data
|
||||
char[] output = encode(input, desc)!;
|
||||
char[] output = new_encode(input, desc)!;
|
||||
|
||||
// open file
|
||||
File! f = file::open(filename, "wb");
|
||||
if (catch f) { return QOIError.FILE_OPEN_FAILED?; }
|
||||
if (catch f) return QOIError.FILE_OPEN_FAILED?;
|
||||
|
||||
// write data to file and close it
|
||||
usz! written = f.write(output);
|
||||
if (catch written) { return QOIError.FILE_WRITE_FAILED?; }
|
||||
if (catch f.close()) { return QOIError.FILE_WRITE_FAILED?; }
|
||||
if (catch written) return QOIError.FILE_WRITE_FAILED?;
|
||||
if (catch f.close()) return QOIError.FILE_WRITE_FAILED?;
|
||||
|
||||
return written;
|
||||
};
|
||||
@@ -112,15 +113,21 @@ fn usz! write(String filename, char[] input, QOIDesc* desc)
|
||||
@param [&out] desc `The descriptor to fill with the image's info`
|
||||
@param channels `The channels to be used`
|
||||
*>
|
||||
fn char[]! read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
|
||||
fn char[]! new_read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
|
||||
{
|
||||
// read file
|
||||
char[]! data = file::load_new(filename);
|
||||
if (catch data) return QOIError.FILE_OPEN_FAILED?;
|
||||
defer mem::free(data);
|
||||
@pool(allocator)
|
||||
{
|
||||
// read file
|
||||
char[] data = file::load_temp(filename) ?? QOIError.FILE_OPEN_FAILED?!;
|
||||
|
||||
// pass data to decode function
|
||||
return decode(data, desc, channels, allocator);
|
||||
// pass data to decode function
|
||||
return new_decode(data, desc, channels, allocator);
|
||||
};
|
||||
}
|
||||
|
||||
fn char[]! read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) @deprecated("Use new_read")
|
||||
{
|
||||
return new_read(filename, desc, channels, allocator);
|
||||
}
|
||||
|
||||
|
||||
@@ -128,6 +135,11 @@ fn char[]! read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, All
|
||||
module std::compression::qoi;
|
||||
import std::bits;
|
||||
|
||||
fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap()) @deprecated("use encode_new")
|
||||
{
|
||||
return new_encode(input, desc, allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Encode raw RGB or RGBA pixels into a QOI image in memory.
|
||||
|
||||
@@ -141,7 +153,7 @@ import std::bits;
|
||||
@param [in] input `The raw RGB or RGBA pixels to encode`
|
||||
@param [&in] desc `The descriptor of the image`
|
||||
*>
|
||||
fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap())
|
||||
fn char[]! new_encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap())
|
||||
{
|
||||
// check info in desc
|
||||
if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_PARAMETERS?;
|
||||
@@ -191,70 +203,77 @@ fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::
|
||||
if (desc.channels == RGBA) p.a = input[loc + 3];
|
||||
|
||||
// check if we can run the previous pixel
|
||||
if (prev == p) {
|
||||
if (prev == p)
|
||||
{
|
||||
run_length++;
|
||||
if (run_length == 62 || loc == loc_end) {
|
||||
*@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 };
|
||||
run_length = 0;
|
||||
}
|
||||
} else {
|
||||
// end last run if there was one
|
||||
if (run_length > 0) {
|
||||
if (run_length == 62 || loc == loc_end)
|
||||
{
|
||||
*@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 };
|
||||
run_length = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// end last run if there was one
|
||||
if (run_length > 0)
|
||||
{
|
||||
*@extract(OpRun, output, &pos) = { OP_RUN, run_length - 1 };
|
||||
run_length = 0;
|
||||
}
|
||||
|
||||
switch {
|
||||
// check if we can index the palette
|
||||
case (palette[p.hash()] == p):
|
||||
*@extract(OpIndex, output, &pos) = {
|
||||
OP_INDEX,
|
||||
p.hash()
|
||||
switch
|
||||
{
|
||||
// check if we can index the palette
|
||||
case (palette[p.hash()] == p):
|
||||
*@extract(OpIndex, output, &pos) = {
|
||||
OP_INDEX,
|
||||
p.hash()
|
||||
};
|
||||
|
||||
// check if we can use diff or luma
|
||||
case (prev != p && prev.a == p.a):
|
||||
// diff the pixels
|
||||
diff = p.rgb - prev.rgb;
|
||||
if (diff.r > -3 && diff.r < 2
|
||||
&& diff.g > -3 && diff.g < 2
|
||||
&& diff.b > -3 && diff.b < 2)
|
||||
{
|
||||
*@extract(OpDiff, output, &pos) = {
|
||||
OP_DIFF,
|
||||
(char)diff.r + 2,
|
||||
(char)diff.g + 2,
|
||||
(char)diff.b + 2
|
||||
};
|
||||
|
||||
// check if we can use diff or luma
|
||||
case (prev != p && prev.a == p.a):
|
||||
// diff the pixels
|
||||
diff = p.rgb - prev.rgb;
|
||||
if (
|
||||
diff.r > -3 && diff.r < 2 &&
|
||||
diff.g > -3 && diff.g < 2 &&
|
||||
diff.b > -3 && diff.b < 2
|
||||
) {
|
||||
*@extract(OpDiff, output, &pos) = {
|
||||
OP_DIFF,
|
||||
(char)diff.r + 2,
|
||||
(char)diff.g + 2,
|
||||
(char)diff.b + 2
|
||||
};
|
||||
palette[p.hash()] = p;
|
||||
} else {
|
||||
// check luma eligibility
|
||||
luma = { diff.r - diff.g, diff.g, diff.b - diff.g };
|
||||
if (
|
||||
luma.r >= -8 && luma.r <= 7 &&
|
||||
luma.g >= -32 && luma.g <= 31 &&
|
||||
luma.b >= -8 && luma.b <= 7
|
||||
) {
|
||||
*@extract(OpLuma, output, &pos) = {
|
||||
OP_LUMA,
|
||||
(char)luma.g + 32,
|
||||
(char)luma.r + 8,
|
||||
(char)luma.b + 8
|
||||
};
|
||||
palette[p.hash()] = p;
|
||||
} else { nextcase; }
|
||||
}
|
||||
|
||||
// worst case scenario: just encode the raw pixel
|
||||
default:
|
||||
if (prev.a != p.a) {
|
||||
*@extract(OpRGBA, output, &pos) = { OP_RGBA, p.r, p.g, p.b, p.a };
|
||||
} else {
|
||||
*@extract(OpRGB, output, &pos) = { OP_RGB, p.r, p.g, p.b };
|
||||
}
|
||||
palette[p.hash()] = p;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// check luma eligibility
|
||||
luma = { diff.r - diff.g, diff.g, diff.b - diff.g };
|
||||
if (luma.r >= -8 && luma.r <= 7
|
||||
&& luma.g >= -32 && luma.g <= 31
|
||||
&& luma.b >= -8 && luma.b <= 7)
|
||||
{
|
||||
*@extract(OpLuma, output, &pos) = {
|
||||
OP_LUMA,
|
||||
(char)luma.g + 32,
|
||||
(char)luma.r + 8,
|
||||
(char)luma.b + 8
|
||||
};
|
||||
palette[p.hash()] = p;
|
||||
break;
|
||||
}
|
||||
nextcase;
|
||||
|
||||
// worst case scenario: just encode the raw pixel
|
||||
default:
|
||||
if (prev.a != p.a)
|
||||
{
|
||||
*@extract(OpRGBA, output, &pos) = { OP_RGBA, p.r, p.g, p.b, p.a };
|
||||
}
|
||||
else
|
||||
{
|
||||
*@extract(OpRGB, output, &pos) = { OP_RGB, p.r, p.g, p.b };
|
||||
}
|
||||
palette[p.hash()] = p;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,6 +285,11 @@ fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::
|
||||
}
|
||||
|
||||
|
||||
fn char[]! decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
|
||||
{
|
||||
return new_decode(data, desc, channels, allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Decode a QOI image from memory.
|
||||
|
||||
@@ -287,7 +311,7 @@ fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::
|
||||
@param [&out] desc `The descriptor to fill with the image's info`
|
||||
@param channels `The channels to be used`
|
||||
*>
|
||||
fn char[]! decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
|
||||
fn char[]! new_decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
|
||||
{
|
||||
// check input data
|
||||
if (data.len < Header.sizeof + END_OF_STREAM.len) return QOIError.INVALID_DATA?;
|
||||
@@ -414,14 +438,16 @@ const char[?] END_OF_STREAM = {0, 0, 0, 0, 0, 0, 0, 1};
|
||||
// inefficient, but it's only run once at a time
|
||||
macro @enumcast($Type, raw)
|
||||
{
|
||||
foreach (value : $Type.values) {
|
||||
foreach (value : $Type.values)
|
||||
{
|
||||
if (value.id == raw) return value;
|
||||
}
|
||||
return QOIError.INVALID_DATA?;
|
||||
}
|
||||
|
||||
distinct Pixel = inline char[<4>];
|
||||
macro char Pixel.hash(Pixel p) {
|
||||
macro char Pixel.hash(Pixel p)
|
||||
{
|
||||
return (p.r * 3 + p.g * 5 + p.b * 7 + p.a * 11) % 64;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,18 +14,18 @@ fn void test_qoi_all()
|
||||
QOIDesc test_desc;
|
||||
|
||||
// decode the test data
|
||||
char[] decoded = qoi::decode(TEST_QOI_DATA[..], &test_desc)!!;
|
||||
char[] decoded = qoi::new_decode(TEST_QOI_DATA[..], &test_desc)!!;
|
||||
assert(test_desc.width == 340 && test_desc.height == 169, "Expected resolution of 340x169");
|
||||
|
||||
// encode the decoded data
|
||||
char[] encoded = qoi::encode(decoded, &test_desc)!!;
|
||||
char[] encoded = qoi::new_encode(decoded, &test_desc)!!;
|
||||
assert(encoded == TEST_QOI_DATA[..], "Encoder output should match the test data");
|
||||
|
||||
// encode and write the decoded data to a file
|
||||
usz written = qoi::write("unittest.qoi", decoded, &test_desc)!!;
|
||||
|
||||
// read and decode the written data
|
||||
char[] read = qoi::read("unittest.qoi", &test_desc)!!;
|
||||
char[] read = qoi::new_read("unittest.qoi", &test_desc)!!;
|
||||
assert(read == decoded, "Read data should match the decoded data");
|
||||
|
||||
// cleanup
|
||||
|
||||
Reference in New Issue
Block a user