mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-07 08:08:50 +00:00
Minor formatting changes
This commit is contained in:
@@ -145,7 +145,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
|||||||
mem.zero_slice(sizes[:]);
|
mem.zero_slice(sizes[:]);
|
||||||
mem.zero_slice(z.fast[:]);
|
mem.zero_slice(z.fast[:]);
|
||||||
|
|
||||||
for v, _ in code_lengths {
|
for v in code_lengths {
|
||||||
sizes[v] += 1;
|
sizes[v] += 1;
|
||||||
}
|
}
|
||||||
sizes[0] = 0;
|
sizes[0] = 0;
|
||||||
@@ -163,7 +163,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
|||||||
z.firstsymbol[i] = u16(k);
|
z.firstsymbol[i] = u16(k);
|
||||||
code = code + sizes[i];
|
code = code + sizes[i];
|
||||||
if sizes[i] != 0 {
|
if sizes[i] != 0 {
|
||||||
if (code - 1 >= (1 << u16(i))) {
|
if code - 1 >= (1 << u16(i)) {
|
||||||
return E_Deflate.Huffman_Bad_Code_Lengths;
|
return E_Deflate.Huffman_Bad_Code_Lengths;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,7 +181,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
|||||||
fastv := u16((u16(v) << 9) | u16(ci));
|
fastv := u16((u16(v) << 9) | u16(ci));
|
||||||
z.size[c] = u8(v);
|
z.size[c] = u8(v);
|
||||||
z.value[c] = u16(ci);
|
z.value[c] = u16(ci);
|
||||||
if (v <= ZFAST_BITS) {
|
if v <= ZFAST_BITS {
|
||||||
j := z_bit_reverse(u16(next_code[v]), v);
|
j := z_bit_reverse(u16(next_code[v]), v);
|
||||||
for j < (1 << ZFAST_BITS) {
|
for j < (1 << ZFAST_BITS) {
|
||||||
z.fast[j] = fastv;
|
z.fast[j] = fastv;
|
||||||
@@ -195,15 +195,10 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
decode_huffman_slowpath :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
decode_huffman_slowpath :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||||
r = 0;
|
|
||||||
err = nil;
|
|
||||||
|
|
||||||
k: int;
|
|
||||||
s: u8;
|
|
||||||
|
|
||||||
code := u16(compress.peek_bits_lsb(z, 16));
|
code := u16(compress.peek_bits_lsb(z, 16));
|
||||||
|
|
||||||
k = int(z_bit_reverse(code, 16));
|
k := int(z_bit_reverse(code, 16));
|
||||||
|
s: u8;
|
||||||
|
|
||||||
#no_bounds_check for s = HUFFMAN_FAST_BITS+1; ; {
|
#no_bounds_check for s = HUFFMAN_FAST_BITS+1; ; {
|
||||||
if k < t.maxcode[s] {
|
if k < t.maxcode[s] {
|
||||||
@@ -211,7 +206,7 @@ decode_huffman_slowpath :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err:
|
|||||||
}
|
}
|
||||||
s += 1;
|
s += 1;
|
||||||
}
|
}
|
||||||
if (s >= 16) {
|
if s >= 16 {
|
||||||
return 0, E_Deflate.Bad_Huffman_Code;
|
return 0, E_Deflate.Bad_Huffman_Code;
|
||||||
}
|
}
|
||||||
// code size is s, so:
|
// code size is s, so:
|
||||||
|
|||||||
+26
-28
@@ -127,7 +127,6 @@ Error :: enum {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
|
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
|
||||||
|
|
||||||
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height;
|
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -144,7 +143,6 @@ Channel :: enum u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {
|
return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {
|
||||||
|
|
||||||
ok = false;
|
ok = false;
|
||||||
t: bytes.Buffer;
|
t: bytes.Buffer;
|
||||||
|
|
||||||
@@ -159,36 +157,36 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok
|
|||||||
return {}, false;
|
return {}, false;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(img.depth) {
|
switch img.depth {
|
||||||
case 8:
|
case 8:
|
||||||
buffer_size := compute_buffer_size(img.width, img.height, 1, 8);
|
buffer_size := compute_buffer_size(img.width, img.height, 1, 8);
|
||||||
t = bytes.Buffer{};
|
t = bytes.Buffer{};
|
||||||
resize(&t.buf, buffer_size);
|
resize(&t.buf, buffer_size);
|
||||||
|
|
||||||
i := bytes.buffer_to_bytes(&img.pixels);
|
i := bytes.buffer_to_bytes(&img.pixels);
|
||||||
o := bytes.buffer_to_bytes(&t);
|
o := bytes.buffer_to_bytes(&t);
|
||||||
|
|
||||||
for len(i) > 0 {
|
for len(i) > 0 {
|
||||||
o[0] = i[idx];
|
o[0] = i[idx];
|
||||||
i = i[img.channels:];
|
i = i[img.channels:];
|
||||||
o = o[1:];
|
o = o[1:];
|
||||||
}
|
}
|
||||||
case 16:
|
case 16:
|
||||||
buffer_size := compute_buffer_size(img.width, img.height, 2, 8);
|
buffer_size := compute_buffer_size(img.width, img.height, 2, 8);
|
||||||
t = bytes.Buffer{};
|
t = bytes.Buffer{};
|
||||||
resize(&t.buf, buffer_size);
|
resize(&t.buf, buffer_size);
|
||||||
|
|
||||||
i := mem.slice_data_cast([]u16, img.pixels.buf[:]);
|
i := mem.slice_data_cast([]u16, img.pixels.buf[:]);
|
||||||
o := mem.slice_data_cast([]u16, t.buf[:]);
|
o := mem.slice_data_cast([]u16, t.buf[:]);
|
||||||
|
|
||||||
for len(i) > 0 {
|
for len(i) > 0 {
|
||||||
o[0] = i[idx];
|
o[0] = i[idx];
|
||||||
i = i[img.channels:];
|
i = i[img.channels:];
|
||||||
o = o[1:];
|
o = o[1:];
|
||||||
}
|
}
|
||||||
case 1, 2, 4:
|
case 1, 2, 4:
|
||||||
// We shouldn't see this case, as the loader already turns these into 8-bit.
|
// We shouldn't see this case, as the loader already turns these into 8-bit.
|
||||||
return {}, false;
|
return {}, false;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = new(Image);
|
res = new(Image);
|
||||||
|
|||||||
+78
-78
@@ -35,86 +35,86 @@ main :: proc() {
|
|||||||
// Handle ancillary chunks as you wish.
|
// Handle ancillary chunks as you wish.
|
||||||
// We provide helper functions for a few types.
|
// We provide helper functions for a few types.
|
||||||
for c in v.chunks {
|
for c in v.chunks {
|
||||||
#partial switch (c.header.type) {
|
#partial switch c.header.type {
|
||||||
case .tIME:
|
case .tIME:
|
||||||
t, _ := png.core_time(c);
|
t, _ := png.core_time(c);
|
||||||
fmt.printf("[tIME]: %v\n", t);
|
fmt.printf("[tIME]: %v\n", t);
|
||||||
case .gAMA:
|
case .gAMA:
|
||||||
fmt.printf("[gAMA]: %v\n", png.gamma(c));
|
fmt.printf("[gAMA]: %v\n", png.gamma(c));
|
||||||
case .pHYs:
|
case .pHYs:
|
||||||
phys := png.phys(c);
|
phys := png.phys(c);
|
||||||
if phys.unit == .Meter {
|
if phys.unit == .Meter {
|
||||||
xm := f32(img.width) / f32(phys.ppu_x);
|
xm := f32(img.width) / f32(phys.ppu_x);
|
||||||
ym := f32(img.height) / f32(phys.ppu_y);
|
ym := f32(img.height) / f32(phys.ppu_y);
|
||||||
dpi_x, dpi_y := png.phys_to_dpi(phys);
|
dpi_x, dpi_y := png.phys_to_dpi(phys);
|
||||||
fmt.printf("[pHYs] Image resolution is %v x %v pixels per meter.\n", phys.ppu_x, phys.ppu_y);
|
fmt.printf("[pHYs] Image resolution is %v x %v pixels per meter.\n", phys.ppu_x, phys.ppu_y);
|
||||||
fmt.printf("[pHYs] Image resolution is %v x %v DPI.\n", dpi_x, dpi_y);
|
fmt.printf("[pHYs] Image resolution is %v x %v DPI.\n", dpi_x, dpi_y);
|
||||||
fmt.printf("[pHYs] Image dimensions are %v x %v meters.\n", xm, ym);
|
fmt.printf("[pHYs] Image dimensions are %v x %v meters.\n", xm, ym);
|
||||||
|
} else {
|
||||||
|
fmt.printf("[pHYs] x: %v, y: %v pixels per unknown unit.\n", phys.ppu_x, phys.ppu_y);
|
||||||
|
}
|
||||||
|
case .iTXt, .zTXt, .tEXt:
|
||||||
|
res, ok_text := png.text(c);
|
||||||
|
if ok_text {
|
||||||
|
if c.header.type == .iTXt {
|
||||||
|
fmt.printf("[iTXt] %v (%v:%v): %v\n", res.keyword, res.language, res.keyword_localized, res.text);
|
||||||
} else {
|
} else {
|
||||||
fmt.printf("[pHYs] x: %v, y: %v pixels per unknown unit.\n", phys.ppu_x, phys.ppu_y);
|
fmt.printf("[tEXt/zTXt] %v: %v\n", res.keyword, res.text);
|
||||||
}
|
}
|
||||||
case .iTXt, .zTXt, .tEXt:
|
}
|
||||||
res, ok_text := png.text(c);
|
defer png.text_destroy(res);
|
||||||
if ok_text {
|
case .bKGD:
|
||||||
if c.header.type == .iTXt {
|
fmt.printf("[bKGD] %v\n", img.background);
|
||||||
fmt.printf("[iTXt] %v (%v:%v): %v\n", res.keyword, res.language, res.keyword_localized, res.text);
|
case .eXIf:
|
||||||
} else {
|
res, ok_exif := png.exif(c);
|
||||||
fmt.printf("[tEXt/zTXt] %v: %v\n", res.keyword, res.text);
|
if ok_exif {
|
||||||
}
|
/*
|
||||||
}
|
Other than checking the signature and byte order, we don't handle Exif data.
|
||||||
defer png.text_destroy(res);
|
If you wish to interpret it, pass it to an Exif parser.
|
||||||
case .bKGD:
|
*/
|
||||||
fmt.printf("[bKGD] %v\n", img.background);
|
fmt.printf("[eXIf] %v\n", res);
|
||||||
case .eXIf:
|
}
|
||||||
res, ok_exif := png.exif(c);
|
case .PLTE:
|
||||||
if ok_exif {
|
plte, plte_ok := png.plte(c);
|
||||||
/*
|
if plte_ok {
|
||||||
Other than checking the signature and byte order, we don't handle Exif data.
|
fmt.printf("[PLTE] %v\n", plte);
|
||||||
If you wish to interpret it, pass it to an Exif parser.
|
} else {
|
||||||
*/
|
fmt.printf("[PLTE] Error\n");
|
||||||
fmt.printf("[eXIf] %v\n", res);
|
}
|
||||||
}
|
case .hIST:
|
||||||
case .PLTE:
|
res, ok_hist := png.hist(c);
|
||||||
plte, plte_ok := png.plte(c);
|
if ok_hist {
|
||||||
if plte_ok {
|
fmt.printf("[hIST] %v\n", res);
|
||||||
fmt.printf("[PLTE] %v\n", plte);
|
}
|
||||||
} else {
|
case .cHRM:
|
||||||
fmt.printf("[PLTE] Error\n");
|
res, ok_chrm := png.chrm(c);
|
||||||
}
|
if ok_chrm {
|
||||||
case .hIST:
|
fmt.printf("[cHRM] %v\n", res);
|
||||||
res, ok_hist := png.hist(c);
|
}
|
||||||
if ok_hist {
|
case .sPLT:
|
||||||
fmt.printf("[hIST] %v\n", res);
|
res, ok_splt := png.splt(c);
|
||||||
}
|
if ok_splt {
|
||||||
case .cHRM:
|
fmt.printf("[sPLT] %v\n", res);
|
||||||
res, ok_chrm := png.chrm(c);
|
}
|
||||||
if ok_chrm {
|
png.splt_destroy(res);
|
||||||
fmt.printf("[cHRM] %v\n", res);
|
case .sBIT:
|
||||||
}
|
if res, ok_sbit := png.sbit(c); ok_sbit {
|
||||||
case .sPLT:
|
fmt.printf("[sBIT] %v\n", res);
|
||||||
res, ok_splt := png.splt(c);
|
}
|
||||||
if ok_splt {
|
case .iCCP:
|
||||||
fmt.printf("[sPLT] %v\n", res);
|
res, ok_iccp := png.iccp(c);
|
||||||
}
|
if ok_iccp {
|
||||||
png.splt_destroy(res);
|
fmt.printf("[iCCP] %v\n", res);
|
||||||
case .sBIT:
|
}
|
||||||
if res, ok_sbit := png.sbit(c); ok_sbit {
|
png.iccp_destroy(res);
|
||||||
fmt.printf("[sBIT] %v\n", res);
|
case .sRGB:
|
||||||
}
|
if res, ok_srgb := png.srgb(c); ok_srgb {
|
||||||
case .iCCP:
|
fmt.printf("[sRGB] Rendering intent: %v\n", res);
|
||||||
res, ok_iccp := png.iccp(c);
|
}
|
||||||
if ok_iccp {
|
case:
|
||||||
fmt.printf("[iCCP] %v\n", res);
|
type := c.header.type;
|
||||||
}
|
name := png.chunk_type_to_name(&type);
|
||||||
png.iccp_destroy(res);
|
fmt.printf("[%v]: %v\n", name, c.data);
|
||||||
case .sRGB:
|
|
||||||
if res, ok_srgb := png.srgb(c); ok_srgb {
|
|
||||||
fmt.printf("[sRGB] Rendering intent: %v\n", res);
|
|
||||||
}
|
|
||||||
case:
|
|
||||||
type := c.header.type;
|
|
||||||
name := png.chunk_type_to_name(&type);
|
|
||||||
fmt.printf("[%v]: %v\n", name, c.data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ core_time :: proc(c: Chunk) -> (t: coretime.Time, ok: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
text :: proc(c: Chunk) -> (res: Text, ok: bool) {
|
text :: proc(c: Chunk) -> (res: Text, ok: bool) {
|
||||||
#partial switch c.header.type {
|
#partial switch c.header.type {
|
||||||
case .tEXt:
|
case .tEXt:
|
||||||
ok = true;
|
ok = true;
|
||||||
|
|
||||||
|
|||||||
+261
-265
@@ -115,11 +115,11 @@ Interlace_Method :: enum u8 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Row_Filter :: enum u8 {
|
Row_Filter :: enum u8 {
|
||||||
None = 0,
|
None = 0,
|
||||||
Sub = 1,
|
Sub = 1,
|
||||||
Up = 2,
|
Up = 2,
|
||||||
Average = 3,
|
Average = 3,
|
||||||
Paeth = 4,
|
Paeth = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
PLTE_Entry :: [3]u8;
|
PLTE_Entry :: [3]u8;
|
||||||
@@ -166,18 +166,18 @@ CIE_1931 :: struct #packed {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cHRM_Raw :: struct #packed {
|
cHRM_Raw :: struct #packed {
|
||||||
w: CIE_1931_Raw,
|
w: CIE_1931_Raw,
|
||||||
r: CIE_1931_Raw,
|
r: CIE_1931_Raw,
|
||||||
g: CIE_1931_Raw,
|
g: CIE_1931_Raw,
|
||||||
b: CIE_1931_Raw,
|
b: CIE_1931_Raw,
|
||||||
}
|
}
|
||||||
#assert(size_of(cHRM_Raw) == 32);
|
#assert(size_of(cHRM_Raw) == 32);
|
||||||
|
|
||||||
cHRM :: struct #packed {
|
cHRM :: struct #packed {
|
||||||
w: CIE_1931,
|
w: CIE_1931,
|
||||||
r: CIE_1931,
|
r: CIE_1931,
|
||||||
g: CIE_1931,
|
g: CIE_1931,
|
||||||
b: CIE_1931,
|
b: CIE_1931,
|
||||||
}
|
}
|
||||||
#assert(size_of(cHRM) == 32);
|
#assert(size_of(cHRM) == 32);
|
||||||
|
|
||||||
@@ -236,10 +236,7 @@ ADAM7_Y_SPACING := []int{ 8,8,8,4,4,2,2 };
|
|||||||
|
|
||||||
// Implementation starts here
|
// Implementation starts here
|
||||||
|
|
||||||
read_chunk :: proc(ctx: ^compress.Context) -> (Chunk, Error) {
|
read_chunk :: proc(ctx: ^compress.Context) -> (chunk: Chunk, err: Error) {
|
||||||
|
|
||||||
chunk := Chunk{};
|
|
||||||
|
|
||||||
ch, e := compress.read_data(ctx, Chunk_Header);
|
ch, e := compress.read_data(ctx, Chunk_Header);
|
||||||
if e != .None {
|
if e != .None {
|
||||||
return {}, E_General.Stream_Too_Short;
|
return {}, E_General.Stream_Too_Short;
|
||||||
@@ -271,7 +268,6 @@ read_chunk :: proc(ctx: ^compress.Context) -> (Chunk, Error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
read_header :: proc(ctx: ^compress.Context) -> (IHDR, Error) {
|
read_header :: proc(ctx: ^compress.Context) -> (IHDR, Error) {
|
||||||
|
|
||||||
c, e := read_chunk(ctx);
|
c, e := read_chunk(ctx);
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return {}, e;
|
return {}, e;
|
||||||
@@ -297,48 +293,48 @@ read_header :: proc(ctx: ^compress.Context) -> (IHDR, Error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (transmute(u8)color_type) {
|
switch transmute(u8)color_type {
|
||||||
case 0:
|
case 0:
|
||||||
/*
|
/*
|
||||||
Grayscale.
|
Grayscale.
|
||||||
Allowed bit depths: 1, 2, 4, 8 and 16.
|
Allowed bit depths: 1, 2, 4, 8 and 16.
|
||||||
*/
|
*/
|
||||||
allowed := false;
|
allowed := false;
|
||||||
for i in ([]u8{1, 2, 4, 8, 16}) {
|
for i in ([]u8{1, 2, 4, 8, 16}) {
|
||||||
if bit_depth == i {
|
if bit_depth == i {
|
||||||
allowed = true;
|
allowed = true;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !allowed {
|
}
|
||||||
return {}, E_PNG.Invalid_Color_Bit_Depth_Combo;
|
if !allowed {
|
||||||
}
|
return {}, E_PNG.Invalid_Color_Bit_Depth_Combo;
|
||||||
case 2, 4, 6:
|
}
|
||||||
/*
|
case 2, 4, 6:
|
||||||
RGB, Grayscale+Alpha, RGBA.
|
/*
|
||||||
Allowed bit depths: 8 and 16
|
RGB, Grayscale+Alpha, RGBA.
|
||||||
*/
|
Allowed bit depths: 8 and 16
|
||||||
if bit_depth != 8 && bit_depth != 16 {
|
*/
|
||||||
return {}, E_PNG.Invalid_Color_Bit_Depth_Combo;
|
if bit_depth != 8 && bit_depth != 16 {
|
||||||
}
|
return {}, E_PNG.Invalid_Color_Bit_Depth_Combo;
|
||||||
case 3:
|
}
|
||||||
/*
|
case 3:
|
||||||
Paletted. PLTE chunk must appear.
|
/*
|
||||||
Allowed bit depths: 1, 2, 4 and 8.
|
Paletted. PLTE chunk must appear.
|
||||||
*/
|
Allowed bit depths: 1, 2, 4 and 8.
|
||||||
allowed := false;
|
*/
|
||||||
for i in ([]u8{1, 2, 4, 8}) {
|
allowed := false;
|
||||||
if bit_depth == i {
|
for i in ([]u8{1, 2, 4, 8}) {
|
||||||
allowed = true;
|
if bit_depth == i {
|
||||||
break;
|
allowed = true;
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
if !allowed {
|
|
||||||
return {}, E_PNG.Invalid_Color_Bit_Depth_Combo;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if !allowed {
|
||||||
|
return {}, E_PNG.Invalid_Color_Bit_Depth_Combo;
|
||||||
|
}
|
||||||
|
|
||||||
case:
|
case:
|
||||||
return {}, E_PNG.Unknown_Color_Type;
|
return {}, E_PNG.Unknown_Color_Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
return header, nil;
|
return header, nil;
|
||||||
@@ -398,11 +394,11 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
|
|||||||
|
|
||||||
img.sidecar = nil;
|
img.sidecar = nil;
|
||||||
|
|
||||||
ctx := compress.Context{
|
ctx := &compress.Context{
|
||||||
input = stream,
|
input = stream,
|
||||||
};
|
};
|
||||||
|
|
||||||
signature, io_error := compress.read_data(&ctx, Signature);
|
signature, io_error := compress.read_data(ctx, Signature);
|
||||||
if io_error != .None || signature != .PNG {
|
if io_error != .None || signature != .PNG {
|
||||||
return img, E_PNG.Invalid_PNG_Signature;
|
return img, E_PNG.Invalid_PNG_Signature;
|
||||||
}
|
}
|
||||||
@@ -435,225 +431,225 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
|
|||||||
|
|
||||||
read_error: io.Error;
|
read_error: io.Error;
|
||||||
// 12 bytes is the size of a chunk with a zero-length payload.
|
// 12 bytes is the size of a chunk with a zero-length payload.
|
||||||
for (read_error == .None && !seen_iend) {
|
for read_error == .None && !seen_iend {
|
||||||
// Peek at next chunk's length and type.
|
// Peek at next chunk's length and type.
|
||||||
// TODO: Some streams may not provide seek/read_at
|
// TODO: Some streams may not provide seek/read_at
|
||||||
|
|
||||||
ch, e = compress.peek_data(&ctx, Chunk_Header);
|
ch, e = compress.peek_data(ctx, Chunk_Header);
|
||||||
if e != .None {
|
if e != .None {
|
||||||
return img, E_General.Stream_Too_Short;
|
return img, E_General.Stream_Too_Short;
|
||||||
}
|
}
|
||||||
// name := chunk_type_to_name(&ch.type); // Only used for debug prints during development.
|
// name := chunk_type_to_name(&ch.type); // Only used for debug prints during development.
|
||||||
|
|
||||||
#partial switch(ch.type) {
|
#partial switch(ch.type) {
|
||||||
case .IHDR:
|
case .IHDR:
|
||||||
if seen_ihdr || !first {
|
if seen_ihdr || !first {
|
||||||
return {}, E_PNG.IHDR_Not_First_Chunk;
|
return {}, E_PNG.IHDR_Not_First_Chunk;
|
||||||
}
|
}
|
||||||
seen_ihdr = true;
|
seen_ihdr = true;
|
||||||
|
|
||||||
header, err = read_header(&ctx);
|
header, err = read_header(ctx);
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return img, err;
|
return img, err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if .Paletted in header.color_type {
|
if .Paletted in header.color_type {
|
||||||
// Color type 3
|
// Color type 3
|
||||||
img.channels = 1;
|
img.channels = 1;
|
||||||
final_image_channels = 3;
|
final_image_channels = 3;
|
||||||
img.depth = 8;
|
img.depth = 8;
|
||||||
} else if .Color in header.color_type {
|
} else if .Color in header.color_type {
|
||||||
// Color image without a palette
|
// Color image without a palette
|
||||||
img.channels = 3;
|
img.channels = 3;
|
||||||
final_image_channels = 3;
|
final_image_channels = 3;
|
||||||
img.depth = header.bit_depth;
|
img.depth = header.bit_depth;
|
||||||
} else {
|
} else {
|
||||||
// Grayscale
|
// Grayscale
|
||||||
img.channels = 1;
|
img.channels = 1;
|
||||||
final_image_channels = 1;
|
final_image_channels = 1;
|
||||||
img.depth = header.bit_depth;
|
img.depth = header.bit_depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
if .Alpha in header.color_type {
|
|
||||||
img.channels += 1;
|
|
||||||
final_image_channels += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if img.channels == 0 || img.depth == 0 {
|
|
||||||
return {}, E_PNG.IHDR_Corrupt;
|
|
||||||
}
|
|
||||||
|
|
||||||
img.width = int(header.width);
|
|
||||||
img.height = int(header.height);
|
|
||||||
|
|
||||||
using header;
|
|
||||||
h := IHDR{
|
|
||||||
width = width,
|
|
||||||
height = height,
|
|
||||||
bit_depth = bit_depth,
|
|
||||||
color_type = color_type,
|
|
||||||
compression_method = compression_method,
|
|
||||||
filter_method = filter_method,
|
|
||||||
interlace_method = interlace_method,
|
|
||||||
};
|
|
||||||
info.header = h;
|
|
||||||
case .PLTE:
|
|
||||||
seen_plte = true;
|
|
||||||
// PLTE must appear before IDAT and can't appear for color types 0, 4.
|
|
||||||
ct := transmute(u8)info.header.color_type;
|
|
||||||
if seen_idat || ct == 0 || ct == 4 {
|
|
||||||
return img, E_PNG.PLTE_Encountered_Unexpectedly;
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err = read_chunk(&ctx);
|
|
||||||
if err != nil {
|
|
||||||
return img, err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.header.length % 3 != 0 || c.header.length > 768 {
|
|
||||||
return img, E_PNG.PLTE_Invalid_Length;
|
|
||||||
}
|
|
||||||
plte_ok: bool;
|
|
||||||
_plte, plte_ok = plte(c);
|
|
||||||
if !plte_ok {
|
|
||||||
return img, E_PNG.PLTE_Invalid_Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if .return_metadata in options {
|
|
||||||
append(&info.chunks, c);
|
|
||||||
}
|
|
||||||
case .IDAT:
|
|
||||||
// If we only want image metadata and don't want the pixel data, we can early out.
|
|
||||||
if .return_metadata not_in options && .do_not_decompress_image in options {
|
|
||||||
img.channels = final_image_channels;
|
|
||||||
img.sidecar = info;
|
|
||||||
return img, nil;
|
|
||||||
}
|
|
||||||
// There must be at least 1 IDAT, contiguous if more.
|
|
||||||
if seen_idat {
|
|
||||||
return img, E_PNG.IDAT_Must_Be_Contiguous;
|
|
||||||
}
|
|
||||||
|
|
||||||
if idat_length > 0 {
|
|
||||||
return img, E_PNG.IDAT_Must_Be_Contiguous;
|
|
||||||
}
|
|
||||||
|
|
||||||
next := ch.type;
|
|
||||||
for next == .IDAT {
|
|
||||||
c, err = read_chunk(&ctx);
|
|
||||||
if err != nil {
|
|
||||||
return img, err;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes.buffer_write(&idat_b, c.data);
|
|
||||||
idat_length += c.header.length;
|
|
||||||
|
|
||||||
ch, e = compress.peek_data(&ctx, Chunk_Header);
|
|
||||||
if e != .None {
|
|
||||||
return img, E_General.Stream_Too_Short;
|
|
||||||
}
|
|
||||||
next = ch.type;
|
|
||||||
}
|
|
||||||
idat = bytes.buffer_to_bytes(&idat_b);
|
|
||||||
if int(idat_length) != len(idat) {
|
|
||||||
return {}, E_PNG.IDAT_Corrupt;
|
|
||||||
}
|
|
||||||
seen_idat = true;
|
|
||||||
case .IEND:
|
|
||||||
c, err = read_chunk(&ctx);
|
|
||||||
if err != nil {
|
|
||||||
return img, err;
|
|
||||||
}
|
|
||||||
seen_iend = true;
|
|
||||||
case .bKGD:
|
|
||||||
|
|
||||||
// TODO: Make sure that 16-bit bKGD + tRNS chunks return u16 instead of u16be
|
|
||||||
|
|
||||||
c, err = read_chunk(&ctx);
|
|
||||||
if err != nil {
|
|
||||||
return img, err;
|
|
||||||
}
|
|
||||||
seen_bkgd = true;
|
|
||||||
if .return_metadata in options {
|
|
||||||
append(&info.chunks, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
ct := transmute(u8)info.header.color_type;
|
|
||||||
switch(ct) {
|
|
||||||
case 3: // Indexed color
|
|
||||||
if c.header.length != 1 {
|
|
||||||
return {}, E_PNG.BKGD_Invalid_Length;
|
|
||||||
}
|
|
||||||
col := _plte.entries[c.data[0]];
|
|
||||||
img.background = [3]u16{
|
|
||||||
u16(col[0]) << 8 | u16(col[0]),
|
|
||||||
u16(col[1]) << 8 | u16(col[1]),
|
|
||||||
u16(col[2]) << 8 | u16(col[2]),
|
|
||||||
};
|
|
||||||
case 0, 4: // Grayscale, with and without Alpha
|
|
||||||
if c.header.length != 2 {
|
|
||||||
return {}, E_PNG.BKGD_Invalid_Length;
|
|
||||||
}
|
|
||||||
col := u16(mem.slice_data_cast([]u16be, c.data[:])[0]);
|
|
||||||
img.background = [3]u16{col, col, col};
|
|
||||||
case 2, 6: // Color, with and without Alpha
|
|
||||||
if c.header.length != 6 {
|
|
||||||
return {}, E_PNG.BKGD_Invalid_Length;
|
|
||||||
}
|
|
||||||
col := mem.slice_data_cast([]u16be, c.data[:]);
|
|
||||||
img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])};
|
|
||||||
}
|
|
||||||
case .tRNS:
|
|
||||||
c, err = read_chunk(&ctx);
|
|
||||||
if err != nil {
|
|
||||||
return img, err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if .Alpha in info.header.color_type {
|
|
||||||
return img, E_PNG.TRNS_Encountered_Unexpectedly;
|
|
||||||
}
|
|
||||||
|
|
||||||
if .return_metadata in options {
|
|
||||||
append(&info.chunks, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
This makes the image one with transparency, so set it to +1 here,
|
|
||||||
even if we need we leave img.channels alone for the defilterer's
|
|
||||||
sake. If we early because the user just cares about metadata,
|
|
||||||
we'll set it to 'final_image_channels'.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
if .Alpha in header.color_type {
|
||||||
|
img.channels += 1;
|
||||||
final_image_channels += 1;
|
final_image_channels += 1;
|
||||||
|
}
|
||||||
|
|
||||||
seen_trns = true;
|
if img.channels == 0 || img.depth == 0 {
|
||||||
if info.header.bit_depth < 8 && .Paletted not_in info.header.color_type {
|
return {}, E_PNG.IHDR_Corrupt;
|
||||||
// Rescale tRNS data so key matches intensity
|
}
|
||||||
dsc := depth_scale_table;
|
|
||||||
scale := dsc[info.header.bit_depth];
|
img.width = int(header.width);
|
||||||
if scale != 1 {
|
img.height = int(header.height);
|
||||||
key := mem.slice_data_cast([]u16be, c.data)[0] * u16be(scale);
|
|
||||||
c.data = []u8{0, u8(key & 255)};
|
using header;
|
||||||
}
|
h := IHDR{
|
||||||
}
|
width = width,
|
||||||
trns = c;
|
height = height,
|
||||||
case .iDOT, .CbGI:
|
bit_depth = bit_depth,
|
||||||
/*
|
color_type = color_type,
|
||||||
iPhone PNG bastardization that doesn't adhere to spec with broken IDAT chunk.
|
compression_method = compression_method,
|
||||||
We're not going to add support for it. If you have the misfortunte of coming
|
filter_method = filter_method,
|
||||||
across one of these files, use a utility to defry it.s
|
interlace_method = interlace_method,
|
||||||
*/
|
};
|
||||||
return img, E_PNG.PNG_Does_Not_Adhere_to_Spec;
|
info.header = h;
|
||||||
case:
|
case .PLTE:
|
||||||
// Unhandled type
|
seen_plte = true;
|
||||||
c, err = read_chunk(&ctx);
|
// PLTE must appear before IDAT and can't appear for color types 0, 4.
|
||||||
|
ct := transmute(u8)info.header.color_type;
|
||||||
|
if seen_idat || ct == 0 || ct == 4 {
|
||||||
|
return img, E_PNG.PLTE_Encountered_Unexpectedly;
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = read_chunk(ctx);
|
||||||
|
if err != nil {
|
||||||
|
return img, err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.header.length % 3 != 0 || c.header.length > 768 {
|
||||||
|
return img, E_PNG.PLTE_Invalid_Length;
|
||||||
|
}
|
||||||
|
plte_ok: bool;
|
||||||
|
_plte, plte_ok = plte(c);
|
||||||
|
if !plte_ok {
|
||||||
|
return img, E_PNG.PLTE_Invalid_Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if .return_metadata in options {
|
||||||
|
append(&info.chunks, c);
|
||||||
|
}
|
||||||
|
case .IDAT:
|
||||||
|
// If we only want image metadata and don't want the pixel data, we can early out.
|
||||||
|
if .return_metadata not_in options && .do_not_decompress_image in options {
|
||||||
|
img.channels = final_image_channels;
|
||||||
|
img.sidecar = info;
|
||||||
|
return img, nil;
|
||||||
|
}
|
||||||
|
// There must be at least 1 IDAT, contiguous if more.
|
||||||
|
if seen_idat {
|
||||||
|
return img, E_PNG.IDAT_Must_Be_Contiguous;
|
||||||
|
}
|
||||||
|
|
||||||
|
if idat_length > 0 {
|
||||||
|
return img, E_PNG.IDAT_Must_Be_Contiguous;
|
||||||
|
}
|
||||||
|
|
||||||
|
next := ch.type;
|
||||||
|
for next == .IDAT {
|
||||||
|
c, err = read_chunk(ctx);
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return img, err;
|
return img, err;
|
||||||
}
|
}
|
||||||
if .return_metadata in options {
|
|
||||||
// NOTE: Chunk cata is currently allocated on the temp allocator.
|
bytes.buffer_write(&idat_b, c.data);
|
||||||
append(&info.chunks, c);
|
idat_length += c.header.length;
|
||||||
|
|
||||||
|
ch, e = compress.peek_data(ctx, Chunk_Header);
|
||||||
|
if e != .None {
|
||||||
|
return img, E_General.Stream_Too_Short;
|
||||||
}
|
}
|
||||||
|
next = ch.type;
|
||||||
|
}
|
||||||
|
idat = bytes.buffer_to_bytes(&idat_b);
|
||||||
|
if int(idat_length) != len(idat) {
|
||||||
|
return {}, E_PNG.IDAT_Corrupt;
|
||||||
|
}
|
||||||
|
seen_idat = true;
|
||||||
|
case .IEND:
|
||||||
|
c, err = read_chunk(ctx);
|
||||||
|
if err != nil {
|
||||||
|
return img, err;
|
||||||
|
}
|
||||||
|
seen_iend = true;
|
||||||
|
case .bKGD:
|
||||||
|
|
||||||
|
// TODO: Make sure that 16-bit bKGD + tRNS chunks return u16 instead of u16be
|
||||||
|
|
||||||
|
c, err = read_chunk(ctx);
|
||||||
|
if err != nil {
|
||||||
|
return img, err;
|
||||||
|
}
|
||||||
|
seen_bkgd = true;
|
||||||
|
if .return_metadata in options {
|
||||||
|
append(&info.chunks, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
ct := transmute(u8)info.header.color_type;
|
||||||
|
switch(ct) {
|
||||||
|
case 3: // Indexed color
|
||||||
|
if c.header.length != 1 {
|
||||||
|
return {}, E_PNG.BKGD_Invalid_Length;
|
||||||
|
}
|
||||||
|
col := _plte.entries[c.data[0]];
|
||||||
|
img.background = [3]u16{
|
||||||
|
u16(col[0]) << 8 | u16(col[0]),
|
||||||
|
u16(col[1]) << 8 | u16(col[1]),
|
||||||
|
u16(col[2]) << 8 | u16(col[2]),
|
||||||
|
};
|
||||||
|
case 0, 4: // Grayscale, with and without Alpha
|
||||||
|
if c.header.length != 2 {
|
||||||
|
return {}, E_PNG.BKGD_Invalid_Length;
|
||||||
|
}
|
||||||
|
col := u16(mem.slice_data_cast([]u16be, c.data[:])[0]);
|
||||||
|
img.background = [3]u16{col, col, col};
|
||||||
|
case 2, 6: // Color, with and without Alpha
|
||||||
|
if c.header.length != 6 {
|
||||||
|
return {}, E_PNG.BKGD_Invalid_Length;
|
||||||
|
}
|
||||||
|
col := mem.slice_data_cast([]u16be, c.data[:]);
|
||||||
|
img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])};
|
||||||
|
}
|
||||||
|
case .tRNS:
|
||||||
|
c, err = read_chunk(ctx);
|
||||||
|
if err != nil {
|
||||||
|
return img, err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if .Alpha in info.header.color_type {
|
||||||
|
return img, E_PNG.TRNS_Encountered_Unexpectedly;
|
||||||
|
}
|
||||||
|
|
||||||
|
if .return_metadata in options {
|
||||||
|
append(&info.chunks, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This makes the image one with transparency, so set it to +1 here,
|
||||||
|
even if we need we leave img.channels alone for the defilterer's
|
||||||
|
sake. If we early because the user just cares about metadata,
|
||||||
|
we'll set it to 'final_image_channels'.
|
||||||
|
*/
|
||||||
|
|
||||||
|
final_image_channels += 1;
|
||||||
|
|
||||||
|
seen_trns = true;
|
||||||
|
if info.header.bit_depth < 8 && .Paletted not_in info.header.color_type {
|
||||||
|
// Rescale tRNS data so key matches intensity
|
||||||
|
dsc := depth_scale_table;
|
||||||
|
scale := dsc[info.header.bit_depth];
|
||||||
|
if scale != 1 {
|
||||||
|
key := mem.slice_data_cast([]u16be, c.data)[0] * u16be(scale);
|
||||||
|
c.data = []u8{0, u8(key & 255)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trns = c;
|
||||||
|
case .iDOT, .CbGI:
|
||||||
|
/*
|
||||||
|
iPhone PNG bastardization that doesn't adhere to spec with broken IDAT chunk.
|
||||||
|
We're not going to add support for it. If you have the misfortunte of coming
|
||||||
|
across one of these files, use a utility to defry it.s
|
||||||
|
*/
|
||||||
|
return img, E_PNG.PNG_Does_Not_Adhere_to_Spec;
|
||||||
|
case:
|
||||||
|
// Unhandled type
|
||||||
|
c, err = read_chunk(ctx);
|
||||||
|
if err != nil {
|
||||||
|
return img, err;
|
||||||
|
}
|
||||||
|
if .return_metadata in options {
|
||||||
|
// NOTE: Chunk cata is currently allocated on the temp allocator.
|
||||||
|
append(&info.chunks, c);
|
||||||
|
}
|
||||||
|
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
@@ -695,7 +691,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
|
|||||||
for p := 0; p < 7; p += 1 {
|
for p := 0; p < 7; p += 1 {
|
||||||
x := (int(header.width) - ADAM7_X_ORIG[p] + ADAM7_X_SPACING[p] - 1) / ADAM7_X_SPACING[p];
|
x := (int(header.width) - ADAM7_X_ORIG[p] + ADAM7_X_SPACING[p] - 1) / ADAM7_X_SPACING[p];
|
||||||
y := (int(header.height) - ADAM7_Y_ORIG[p] + ADAM7_Y_SPACING[p] - 1) / ADAM7_Y_SPACING[p];
|
y := (int(header.height) - ADAM7_Y_ORIG[p] + ADAM7_Y_SPACING[p] - 1) / ADAM7_Y_SPACING[p];
|
||||||
if (x > 0 && y > 0) {
|
if x > 0 && y > 0 {
|
||||||
expected_size += compute_buffer_size(int(x), int(y), int(img.channels), int(header.bit_depth), 1);
|
expected_size += compute_buffer_size(int(x), int(y), int(img.channels), int(header.bit_depth), 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -854,7 +850,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
|
|||||||
p16 := mem.slice_data_cast([]u16, temp.buf[:]);
|
p16 := mem.slice_data_cast([]u16, temp.buf[:]);
|
||||||
o16 := mem.slice_data_cast([]u16, t.buf[:]);
|
o16 := mem.slice_data_cast([]u16, t.buf[:]);
|
||||||
|
|
||||||
switch (raw_image_channels) {
|
switch raw_image_channels {
|
||||||
case 1:
|
case 1:
|
||||||
// Gray without Alpha. Might have tRNS alpha.
|
// Gray without Alpha. Might have tRNS alpha.
|
||||||
key := u16(0);
|
key := u16(0);
|
||||||
@@ -1051,7 +1047,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c
|
|||||||
p := mem.slice_data_cast([]u8, temp.buf[:]);
|
p := mem.slice_data_cast([]u8, temp.buf[:]);
|
||||||
o := mem.slice_data_cast([]u8, t.buf[:]);
|
o := mem.slice_data_cast([]u8, t.buf[:]);
|
||||||
|
|
||||||
switch (raw_image_channels) {
|
switch raw_image_channels {
|
||||||
case 1:
|
case 1:
|
||||||
// Gray without Alpha. Might have tRNS alpha.
|
// Gray without Alpha. Might have tRNS alpha.
|
||||||
key := u8(0);
|
key := u8(0);
|
||||||
@@ -1276,7 +1272,7 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) {
|
|||||||
nk := row_stride - channels;
|
nk := row_stride - channels;
|
||||||
|
|
||||||
filter := Row_Filter(src[0]); src = src[1:];
|
filter := Row_Filter(src[0]); src = src[1:];
|
||||||
switch(filter) {
|
switch filter {
|
||||||
case .None:
|
case .None:
|
||||||
copy(dest, src[:row_stride]);
|
copy(dest, src[:row_stride]);
|
||||||
case .Sub:
|
case .Sub:
|
||||||
@@ -1590,7 +1586,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option
|
|||||||
i,j,x,y: int;
|
i,j,x,y: int;
|
||||||
x = (width - ADAM7_X_ORIG[p] + ADAM7_X_SPACING[p] - 1) / ADAM7_X_SPACING[p];
|
x = (width - ADAM7_X_ORIG[p] + ADAM7_X_SPACING[p] - 1) / ADAM7_X_SPACING[p];
|
||||||
y = (height - ADAM7_Y_ORIG[p] + ADAM7_Y_SPACING[p] - 1) / ADAM7_Y_SPACING[p];
|
y = (height - ADAM7_Y_ORIG[p] + ADAM7_Y_SPACING[p] - 1) / ADAM7_Y_SPACING[p];
|
||||||
if (x > 0 && y > 0) {
|
if x > 0 && y > 0 {
|
||||||
temp: bytes.Buffer;
|
temp: bytes.Buffer;
|
||||||
temp_len := compute_buffer_size(x, y, channels, depth == 16 ? 16 : 8);
|
temp_len := compute_buffer_size(x, y, channels, depth == 16 ? 16 : 8);
|
||||||
resize(&temp.buf, temp_len);
|
resize(&temp.buf, temp_len);
|
||||||
|
|||||||
Reference in New Issue
Block a user