From 291a0e3a5d1f67405173e0fa311e19f1b6f48843 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sat, 1 Jan 2022 23:50:23 +0100 Subject: [PATCH] QOI support https://qoiformat.org/ --- src/pixie.nim | 13 +- src/pixie/fileformats/qoi.nim | 209 ++++++++++++++++++++++++ tests/all.nim | 1 + tests/benchmark_qoi.nim | 6 + tests/fileformats/qoi/testcard.png | Bin 0 -> 14227 bytes tests/fileformats/qoi/testcard.qoi | Bin 0 -> 21857 bytes tests/fileformats/qoi/testcard_rgba.png | Bin 0 -> 18371 bytes tests/fileformats/qoi/testcard_rgba.qoi | Bin 0 -> 24167 bytes tests/fuzz_qoi.nim | 25 +++ tests/test_qoi.nim | 15 ++ 10 files changed, 266 insertions(+), 3 deletions(-) create mode 100644 src/pixie/fileformats/qoi.nim create mode 100644 tests/benchmark_qoi.nim create mode 100644 tests/fileformats/qoi/testcard.png create mode 100644 tests/fileformats/qoi/testcard.qoi create mode 100644 tests/fileformats/qoi/testcard_rgba.png create mode 100644 tests/fileformats/qoi/testcard_rgba.qoi create mode 100644 tests/fuzz_qoi.nim create mode 100644 tests/test_qoi.nim diff --git a/src/pixie.nim b/src/pixie.nim index a9fac34..7afede4 100644 --- a/src/pixie.nim +++ b/src/pixie.nim @@ -1,13 +1,14 @@ import bumpy, chroma, flatty/binny, os, pixie/common, pixie/contexts, pixie/fileformats/bmp, pixie/fileformats/gif, pixie/fileformats/jpg, - pixie/fileformats/png, pixie/fileformats/svg, pixie/fonts, pixie/images, - pixie/masks, pixie/paints, pixie/paths, strutils, vmath + pixie/fileformats/png, pixie/fileformats/qoi, pixie/fileformats/svg, + pixie/fonts, pixie/images, pixie/masks, pixie/paints, pixie/paths, + strutils, vmath export bumpy, chroma, common, contexts, fonts, images, masks, paints, paths, vmath type FileFormat* = enum - ffPng, ffBmp, ffJpg, ffGif + ffPng, ffBmp, ffJpg, ffGif, ffQoi converter autoStraightAlpha*(c: ColorRGBX): ColorRGBA {.inline, raises: [].} = ## Convert a premultiplied alpha RGBA to a straight alpha RGBA. @@ -30,6 +31,8 @@ proc decodeImage*(data: string | seq[uint8]): Image {.raises: [PixieError].} = decodeSvg(data) elif data.len > 6 and data.readStr(0, 6) in gifSignatures: decodeGif(data) + elif data.len > (14+8) and data.readStr(0, 4) == qoiSignature: + decodeQoi(data) else: raise newException(PixieError, "Unsupported image file format") @@ -63,6 +66,8 @@ proc encodeImage*(image: Image, fileFormat: FileFormat): string {.raises: [Pixie image.encodeJpg() of ffBmp: image.encodeBmp() + of ffQoi: + image.encodeQoi() of ffGif: raise newException(PixieError, "Unsupported file format") @@ -80,6 +85,7 @@ proc writeFile*(image: Image, filePath: string) {.raises: [PixieError].} = of ".png": ffPng of ".bmp": ffBmp of ".jpg", ".jpeg": ffJpg + of ".qoi": ffQoi else: raise newException(PixieError, "Unsupported file extension") @@ -94,6 +100,7 @@ proc writeFile*(mask: Mask, filePath: string) {.raises: [PixieError].} = of ".png": ffPng of ".bmp": ffBmp of ".jpg", ".jpeg": ffJpg + of ".qoi": ffQoi else: raise newException(PixieError, "Unsupported file extension") diff --git a/src/pixie/fileformats/qoi.nim b/src/pixie/fileformats/qoi.nim new file mode 100644 index 0000000..12a6498 --- /dev/null +++ b/src/pixie/fileformats/qoi.nim @@ -0,0 +1,209 @@ +import std/endians, chroma, flatty/binny +import pixie/[common, images, internal] + +# See: https://qoiformat.org/qoi-specification.pdf + +const + qoiSignature* = "qoif" + indexLen = 64 + opRgb = 0b11111110'u8 + opRgba = 0b11111111'u8 + opMask2 = 0b11000000'u8 + opIndex = 0b00000000'u8 + opDiff = 0b01000000'u8 + opLuma = 0b10000000'u8 + opRun = 0b11000000'u8 + +type + Colorspace* = enum sRBG = 0, linear = 1 + Qoi* = ref object + ## Raw QOI image data. + data*: seq[ColorRGBA] + width*, height*, channels*: int + colorspace*: Colorspace + + Index = array[indexLen, ColorRGBA] + +func hash(p: ColorRGBA): int = + (p.r.int * 3 + p.g.int * 5 + p.b.int * 7 + p.a.int * 11) mod indexLen + +func toImage*(qoi: Qoi): Image = + ## Converts raw QOI data to `Image`. + result = newImage(qoi.width, qoi.height) + copyMem(result.data[0].addr, qoi.data[0].addr, qoi.data.len * 4) + result.data.toPremultipliedAlpha() + +func toQoi*(img: Image; channels: range[3..4]): Qoi = + ## Converts an `Image` to raw QOI data. + result = Qoi( + data: newSeq[ColorRGBA](img.data.len), + width: img.width, + height: img.height, + channels: channels) + result.data.toStraightAlpha() + +proc decompressQoi*(data: string): Qoi {.raises: [PixieError].} = + ## Decompress QOI file format data. + if data.len <= 14 or data[0 .. 3] != qoiSignature: + raise newException(PixieError, "Invalid QOI header") + var + width, height: uint32 + channels, colorspace: uint8 + block: + when cpuEndian == bigEndian: + width = data.readUint32(4) + height = data.readUint32(8) + else: + var (wBe, hBe) = (data.readUint32(4), data.readUint32(8)) + swapEndian32(addr width, addr wBe) + swapEndian32(addr height, addr hBe) + channels = data.readUint8(12) + colorspace = data.readUint8(13) + if channels notin {3, 4} or colorspace notin {0, 1}: + raise newException(PixieError, "Invalid QOI header") + if width.int * height.int > uint32.high.int: + raise newException(PixieError, "QOI is too large to decode") + + result = Qoi( + data: newSeq[ColorRGBA](int width * height), + width: int width, + height: int height, + channels: int channels, + colorspace: Colorspace colorspace) + + var + index: Index + p = 14 + run: uint8 + px = rgba(0, 0, 0, 0xff) + + for dst in result.data.mitems: + if p > data.len-8: + raise newException(PixieError, "Underrun of QOI decoder") + if run > 0: + dec(run) + else: + let b0 = data.readUint8(p) + inc(p) + case b0 + of opRgb: + px.r = data.readUint8(p+0) + px.g = data.readUint8(p+1) + px.b = data.readUint8(p+2) + inc(p, 3) + of opRgba: + px.r = data.readUint8(p+0) + px.g = data.readUint8(p+1) + px.b = data.readUint8(p+2) + px.a = data.readUint8(p+3) + inc(p, 4) + else: + case b0 and opMask2 + of opIndex: + px = index[b0] + of opDiff: + px.r = px.r + uint8((b0 shr 4) and 0x03) - 2 + px.g = px.g + uint8((b0 shr 2) and 0x03) - 2 + px.b = px.b + uint8((b0 shr 0) and 0x03) - 2 + of opLuma: + let b1 = data.readUint8(p) + inc(p) + let vg = (b0.uint8 and 0x3f) - 32 + px.r = px.r + vg - 8 + ((b1 shr 4) and 0x0f) + px.g = px.g + vg + px.b = px.b + vg - 8 + ((b1 shr 0) and 0x0f) + of opRun: + run = b0 and 0x3f + else: assert false + index[hash(px)] = px + dst = px + while p < data.len: + case data[p] + of '\0': discard + of '\1': break # ignore trailing data + else: + raise newException(PixieError, "Invalid QOI padding") + inc(p) + +proc decodeQoi*(data: string): Image {.raises: [PixieError].} = + ## Decodes data in the QOI file format to an `Image`. + decompressQoi(data).toImage() + +proc decodeQoi*(data: seq[uint8]): Image {.inline, raises: [PixieError].} = + ## Decodes data in the QOI file format to an `Image`. + decodeQoi(cast[string](data)) + +proc compressQoi*(qoi: Qoi): string = + ## Encodes raw QOI pixels to the QOI file format. + result = newStringOfCap(14 + 8 + qoi.data.len * 3) + # allocate a buffer 3/4 the size of the pathological encoding + result.add(qoiSignature) + when cpuEndian == bigEndian: + result.addUint32(uint32 qoi.width) + result.addUint32(uint32 qoi.height) + else: + var + (wLe, hLe) = (uint32 qoi.width, uint32 qoi.height) + result.setLen(12) + swapEndian32(addr result[4], addr wLe) + swapEndian32(addr result[8], addr hLe) + result.addUint8(uint8 qoi.channels) + result.addUint8(uint8 qoi.colorspace) + + var + index: Index + run: uint8 + pxPrev = rgba(0, 0, 0, 0xff) + + for off, px in qoi.data: + if px == pxPrev: + inc run + if run == 62 or off == qoi.data.high: + result.addUint8(opRun or pred(run)) + reset run + else: + if run > 0: + result.addUint8(opRun or pred(run)) + reset run + let i = hash(px) + if index[i] == px: result.addUint8(opIndex or uint8(i)) + else: + index[i] = px + if px.a == pxPrev.a: + let + vr = px.r.int - pxPrev.r.int + vg = px.g.int - pxPrev.g.int + vb = px.b.int - pxPrev.b.int + vgr = vr - vg + vgb = vb - vg + if (vr > -3) and (vr < 2) and + (vg > -3) and (vg < 2) and + (vb > -3) and (vb < 2): + let b = opDiff or uint8( + ((vr + 2) shl 4) or + ((vg + 2) shl 2) or + ((vb + 2) shl 0)) + result.addUint8(b) + elif vgr > -9 and vgr < 8 and + vg > -33 and vg < 32 and + vgb > -9 and vgb < 8: + result.addUint8(opLuma or uint8(vg + 32)) + result.addUint8(uint8 ((vgr + 8) shl 4) or (vgb + 8)) + else: + result.addUint8(opRgb) + result.addUint8(px.r) + result.addUint8(px.g) + result.addUint8(px.b) + else: + result.addUint8(opRgba) + result.addUint8(px.r) + result.addUint8(px.g) + result.addUint8(px.b) + result.addUint8(px.a) + pxPrev = px + for _ in 0..6: result.addUint8(0x00) + result.addUint8(0x01) + +proc encodeQoi*(img: Image): string {.raises: [].} = + ## Encodes an image to the QOI file format. + compressQoi(toQoi(img, 4)) diff --git a/tests/all.nim b/tests/all.nim index e0c13a0..69850f8 100644 --- a/tests/all.nim +++ b/tests/all.nim @@ -10,6 +10,7 @@ import test_paints, test_paths, test_png, + test_qoi, test_svg, ../examples/text, ../examples/text_spans, diff --git a/tests/benchmark_qoi.nim b/tests/benchmark_qoi.nim new file mode 100644 index 0000000..616f317 --- /dev/null +++ b/tests/benchmark_qoi.nim @@ -0,0 +1,6 @@ +import benchy, pixie/fileformats/qoi + +let data = readFile("tests/fileformats/qoi/testcard_rgba.qoi") + +timeIt "pixie decode": + keep decodeQOI(data) diff --git a/tests/fileformats/qoi/testcard.png b/tests/fileformats/qoi/testcard.png new file mode 100644 index 0000000000000000000000000000000000000000..8b0cf5ff7de25261c85220773363e374d0e5f3dc GIT binary patch literal 14227 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 literal 0 HcmV?d00001 diff --git a/tests/fuzz_qoi.nim b/tests/fuzz_qoi.nim new file mode 100644 index 0000000..9149822 --- /dev/null +++ b/tests/fuzz_qoi.nim @@ -0,0 +1,25 @@ +import std/[random, strformat] +import pixie/[common, fileformats/qoi] + +randomize() + +let original = readFile("tests/fileformats/qoi/testcard_rgba.qoi") + +for i in 0 ..< 10_000: + var data = original + let + pos = rand(data.len) + value = rand(255).char + data[pos] = value + try: + let img = decodeQOI(data) + doAssert img.height > 0 and img.width > 0 + except PixieError: + discard + + data = data[0 ..< pos] + try: + let img = decodeQOI(data) + doAssert img.height > 0 and img.width > 0 + except PixieError: + discard diff --git a/tests/test_qoi.nim b/tests/test_qoi.nim new file mode 100644 index 0000000..90ab472 --- /dev/null +++ b/tests/test_qoi.nim @@ -0,0 +1,15 @@ +import pixie, pixie/fileformats/qoi, pixie/fileformats/png + +const tests = ["testcard", "testcard_rgba"] + +for name in tests: + var input = decodeQoi(readFile("tests/fileformats/qoi/" & name & ".qoi")) + var control = decodePng(readFile("tests/fileformats/qoi/" & name & ".png")) + doAssert(input.data == control.data, "input mismatch of " & name) + +for name in tests: + var + input: Qoi = decompressQoi(readFile("tests/fileformats/qoi/" & name & ".qoi")) + output: Qoi = decompressQoi(compressQoi(input)) + doAssert(output.data.len == input.data.len) + doAssert(output.data == input.data)