qoiDecode function
Decode Uint8List to a QoiFile with the given PixelFormat as an output pixel format.
PixelFormat can be any of PixelFormat.RGB (0xRRGGBB), PixelFormat.ARGB (0xAARRGGBB) or PixelFormat.RGBA (0xRRGGBBAA).
Returns a QoiFile with information such as width, height, etc.
Implementation
QoiFile qoiDecode(Uint8List bytes, PixelFormat pixelFormat) {
int readIndex = 0;
int readByte() {
return bytes[readIndex++];
}
int readInt32() {
return readByte() << 24 | readByte() << 16 | readByte() << 8 | readByte();
}
if (bytes.length < 23 || readByte() != 113 || readByte() != 111 || readByte() != 105 || readByte() != 102) {
throw "Invalid QOI file";
}
if (bytes[bytes.length - 1] != 1 || bytes[bytes.length - 2] != 0
|| bytes[bytes.length - 3] != 0|| bytes[bytes.length - 4] != 0
|| bytes[bytes.length - 5] != 0|| bytes[bytes.length - 6] != 0
|| bytes[bytes.length - 7] != 0|| bytes[bytes.length - 8] != 0) {
throw "Invalid QOI file";
}
int width = readInt32();
int height = readInt32();
if (width <= 0 || height <= 0 || height > 2147483647 / width) {
throw "Invalid width / height";
}
int channels = readByte();
bool hasAlpha = false;
if (channels == 3) {
}
else if (channels == 4) {
hasAlpha = true;
}
else {
throw "Invalid channels";
}
int colorSpace = readByte();
bool linearColorSpace = false;
if (colorSpace == 0) {
}
else if (colorSpace == 1) {
linearColorSpace = true;
}
else {
throw "Invalid color space";
}
final outChannels = pixelFormat == PixelFormat.RGB ? 3 : 4;
final outBytes = Uint8List(width * height * outChannels);
int writeIndex = 0;
final pixelIndex = Uint32List.fromList(List.filled(64, 0));
int prevPixelR = 0;
int prevPixelG = 0;
int prevPixelB = 0;
int prevPixelA = 255;
void writeColor(int r, int g, int b, int a) {
if (pixelFormat == PixelFormat.RGBA) {
outBytes[writeIndex++] = r & 0x000000FF;
outBytes[writeIndex++] = g & 0x000000FF;
outBytes[writeIndex++] = b & 0x000000FF;
outBytes[writeIndex++] = a & 0x000000FF;
}
else if (pixelFormat == PixelFormat.ARGB) {
outBytes[writeIndex++] = a & 0x000000FF;
outBytes[writeIndex++] = r & 0x000000FF;
outBytes[writeIndex++] = g & 0x000000FF;
outBytes[writeIndex++] = b & 0x000000FF;
}
else {
outBytes[writeIndex++] = r & 0x000000FF;
outBytes[writeIndex++] = g & 0x000000FF;
outBytes[writeIndex++] = b & 0x000000FF;
}
}
while (readIndex < bytes.length - 8) {
int op = readByte();
if (op == _QOI_OP_RGB) {
int r = readByte();
int g = readByte();
int b = readByte();
int a = prevPixelA;
writeColor(r, g, b, a);
prevPixelR = r;
prevPixelG = g;
prevPixelB = b;
prevPixelA = a;
pixelIndex[(r * 3 + g * 5 + b * 7 + a * 11) % 64] = r << 24 | g << 16 | b << 8 | a;
}
else if (op == _QOI_OP_RGBA) {
int r = readByte();
int g = readByte();
int b = readByte();
int a = readByte();
writeColor(r, g, b, a);
prevPixelR = r;
prevPixelG = g;
prevPixelB = b;
prevPixelA = a;
pixelIndex[(r * 3 + g * 5 + b * 7 + a * 11) % 64] = r << 24 | g << 16 | b << 8 | a;
}
else if (op >> 6 == _QOI_OP_INDEX) {
int index = op & 0x3F;
int color = pixelIndex[index];
int r = (color & 0xFF000000) >> 24;
int g = (color & 0x00FF0000) >> 16;
int b = (color & 0x0000FF00) >> 8;
int a = (color & 0x000000FF);
writeColor(r, g, b, a);
prevPixelR = r;
prevPixelG = g;
prevPixelB = b;
prevPixelA = a;
}
else if (op >> 6 == _QOI_OP_DIFF) {
int r = (prevPixelR + ((op & 0x30) >> 4) - 2) % 256;
int g = (prevPixelG + ((op & 0x0C) >> 2) - 2) % 256;
int b = (prevPixelB + (op & 0x03) - 2) % 256;
int a = prevPixelA;
writeColor(r, g, b, a);
prevPixelR = r;
prevPixelG = g;
prevPixelB = b;
prevPixelA = a;
pixelIndex[(r * 3 + g * 5 + b * 7 + a * 11) % 64] = r << 24 | g << 16 | b << 8 | a;
}
else if (op >> 6 == _QOI_OP_LUMA) {
int byte = readByte();
int dg = (op & 0x3F) - 32;
int dr = ((byte & 0xF0) >> 4) + dg;
int db = (byte & 0x0F) + dg;
int r = (prevPixelR + dr - 8) % 256;
int g = (prevPixelG + dg) % 256;
int b = (prevPixelB + db - 8) % 256;
int a = prevPixelA;
writeColor(r, g, b, a);
prevPixelR = r;
prevPixelG = g;
prevPixelB = b;
prevPixelA = a;
pixelIndex[(r * 3 + g * 5 + b * 7 + a * 11) % 64] = r << 24 | g << 16 | b << 8 | a;
}
else if (op >> 6 == _QOI_OP_RUN) {
int run = (op & 0x3F) + 1;
for (int a = 0; a < run; a++) {
writeColor(prevPixelR, prevPixelG, prevPixelB, prevPixelA);
}
}
}
return QoiFile(width, height, outBytes, hasAlpha, linearColorSpace);
}