一个QOI文件包含一个14字节的头,头后跟任意数量的数据“块”和一个8字节的结束标记。
qoi_header {
char magic[4]; // magic bytes "qoif"
uint32_t width; // image width in pixels (BE)
uint32_t height; // image height in pixels (BE)
uint8_t channels; // 3 = RGB, 4 = RGBA
uint8_t colorspace; // 0 = sRGB with linear alpha
}; // 1 = all channels linear
colorspace和channel域只提供信息,都不会改变编/解码器的行为。
图像从左到右、从上到下逐行编码。解码器和编码器以{r: 0,g: 0,b: 0,a: 255}作为前一个像素值的开始。当width * height指定所有像素都被覆盖时,图像就是完整的。像素编码为:
- 上一个像素的run(参考Run-Length-Code)
- 以前看到的像素阵列的索引
- 与r、g、b中先前像素值的差异
- 全r、g、b或r、g、b、a值
假设颜色通道没有与alpha通道预相乘(“未预相乘的alpha”)。
编码器和解码器保持先前看到的像素值的 running array[64]。编码器和解码器看到的每个像素被放入这个数组中由color值通过hash形成的位置。在编码器中,如果索引处的像素值与当前像素匹配,该索引位置的数据将作为QOI \_OP\_INDEX写入流中。索引的hash函数为:
index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64
每个块以一个2位或8位的tag为开始,后面跟着一些数据位。块的比特长度可被8整除,即所有块都是字节对齐的。数据采用小端编码。8位tag的块优先于2位tag的。解码器必须首先检查8位tag块的存在。
字节流的结尾标记为7个0x00字节,后跟一个0x01字节。
可能的块格式有:
八位 tag 11111110
八位 red 通道值
八位 green 通道值
八位 blue 通道值
alpha值与上一个像素相同。
八位 tag b11111111
八位 red 通道值
八位 green 通道值
八位 blue 通道值
八位 alpha 通道值
2位 tag b00
6位 颜色索引数组的索引:0. .63
有效的编码器不得向索引0发出7个或更多连续的“QOI\_OP\_INDEX”块,以避免与结束标记混淆。
2位 tag b01
2位 与前一个像素的red 通道的差值 -2 . . 1
2位 与前一个像素的green 通道的差值 -2 . . 1
2位 与前一个像素的blue 通道的差值 -2 . . 1
与当前通道值的差值采用环绕式处理,因此1- 2 = 255,而255 + 1 = 0。
值存储为偏差为2的无符号整数。例如,-2存储为0(b00)。1存储为3(b11)。
alpha值与上一个像素的相同。
2位 tag b10
6位 与前一个像素的red 通道的差值 -32 . . 31
4位 red 通道差减去green 通道的差值 -8 . . 7
4位 blue 通道差减去green 通道的差值 -8 . . 7
用6位编码,以绿色通道的差值指示变化的大致方向。red和blue通道(dr和db)的差值基于green通道的差值。即:
dr_dg = (last_px.r - cur_px.r) - (last_px.g - cur_px.g)
db_dg = (last_px.b - cur_px.b) - (last_px.g - cur_px.g)
与当前通道值的差值采用环绕式处理,因此10 - 13 = 253,而250 + 7 = 1。
差值存储为无符号整数,green通道的偏差为32,红色和蓝色通道的偏差为8。
alpha值与与上一个像素相同。
2位 tag b11
6位 重复前一个像素的游程长度:1 . . 62
游程长度以-1的偏差存储。请注意,游程长度63和64 (b111110和b11111)是非法的,因为已被QOI\_OP\_RGB和QOI\_OP\_RGBA标记占用。
原文 :The Quite OK Image Format Specification
版本出处:Version 1.0, 2021.12.21 – http://qoiformat.org – Dominic Szablewski