Minor stylistic code changes to compress and image packages

This commit is contained in:
gingerBill
2021-04-30 10:58:29 +01:00
parent 7ef30355cb
commit 5f617c56e1
8 changed files with 584 additions and 589 deletions
+1 -1
View File
@@ -200,4 +200,4 @@ read_bits_no_refill_lsb :: #force_inline proc(z: ^Context, width: u8) -> u32 {
discard_to_next_byte_lsb :: proc(z: ^Context) { discard_to_next_byte_lsb :: proc(z: ^Context) {
discard := u8(z.num_bits & 7); discard := u8(z.num_bits & 7);
consume_bits_lsb(z, discard); consume_bits_lsb(z, discard);
} }
+3 -3
View File
@@ -34,7 +34,7 @@ main :: proc() {
if len(args) < 2 { if len(args) < 2 {
stderr("No input file specified.\n"); stderr("No input file specified.\n");
err := gzip.load(&TEST, &buf); err := gzip.load(TEST, &buf);
if gzip.is_kind(err, gzip.E_General.OK) { if gzip.is_kind(err, gzip.E_General.OK) {
stdout("Displaying test vector: "); stdout("Displaying test vector: ");
stdout(bytes.buffer_to_string(&buf)); stdout(bytes.buffer_to_string(&buf));
@@ -50,7 +50,7 @@ main :: proc() {
if file == "-" { if file == "-" {
// Read from stdin // Read from stdin
s := os.stream_from_handle(os.stdin); s := os.stream_from_handle(os.stdin);
err = gzip.load(&s, &buf); err = gzip.load(s, &buf);
} else { } else {
err = gzip.load(file, &buf); err = gzip.load(file, &buf);
} }
@@ -67,4 +67,4 @@ main :: proc() {
stdout(bytes.buffer_to_string(&buf)); stdout(bytes.buffer_to_string(&buf));
} }
os.exit(0); os.exit(0);
} }
+38 -40
View File
@@ -45,39 +45,39 @@ Header_Flag :: enum u8 {
Header_Flags :: distinct bit_set[Header_Flag; u8]; Header_Flags :: distinct bit_set[Header_Flag; u8];
OS :: enum u8 { OS :: enum u8 {
FAT = 0, FAT = 0,
Amiga = 1, Amiga = 1,
VMS = 2, VMS = 2,
Unix = 3, Unix = 3,
VM_CMS = 4, VM_CMS = 4,
Atari_TOS = 5, Atari_TOS = 5,
HPFS = 6, HPFS = 6,
Macintosh = 7, Macintosh = 7,
Z_System = 8, Z_System = 8,
CP_M = 9, CP_M = 9,
TOPS_20 = 10, TOPS_20 = 10,
NTFS = 11, NTFS = 11,
QDOS = 12, QDOS = 12,
Acorn_RISCOS = 13, Acorn_RISCOS = 13,
_Unknown = 14, _Unknown = 14,
Unknown = 255, Unknown = 255,
} }
OS_Name :: #partial [OS]string{ OS_Name :: #partial [OS]string{
.FAT = "FAT", .FAT = "FAT",
.Amiga = "Amiga", .Amiga = "Amiga",
.VMS = "VMS/OpenVMS", .VMS = "VMS/OpenVMS",
.Unix = "Unix", .Unix = "Unix",
.VM_CMS = "VM/CMS", .VM_CMS = "VM/CMS",
.Atari_TOS = "Atari TOS", .Atari_TOS = "Atari TOS",
.HPFS = "HPFS", .HPFS = "HPFS",
.Macintosh = "Macintosh", .Macintosh = "Macintosh",
.Z_System = "Z-System", .Z_System = "Z-System",
.CP_M = "CP/M", .CP_M = "CP/M",
.TOPS_20 = "TOPS-20", .TOPS_20 = "TOPS-20",
.NTFS = "NTFS", .NTFS = "NTFS",
.QDOS = "QDOS", .QDOS = "QDOS",
.Acorn_RISCOS = "Acorn RISCOS", .Acorn_RISCOS = "Acorn RISCOS",
.Unknown = "Unknown", .Unknown = "Unknown",
}; };
Compression :: enum u8 { Compression :: enum u8 {
@@ -96,13 +96,13 @@ E_ZLIB :: compress.ZLIB_Error;
E_Deflate :: compress.Deflate_Error; E_Deflate :: compress.Deflate_Error;
is_kind :: compress.is_kind; is_kind :: compress.is_kind;
load_from_slice :: proc(slice: ^[]u8, buf: ^bytes.Buffer, allocator := context.allocator) -> (err: Error) { load_from_slice :: proc(slice: []u8, buf: ^bytes.Buffer, allocator := context.allocator) -> (err: Error) {
r := bytes.Reader{}; r := bytes.Reader{};
bytes.reader_init(&r, slice^); bytes.reader_init(&r, slice);
stream := bytes.reader_to_stream(&r); stream := bytes.reader_to_stream(&r);
err = load_from_stream(&stream, buf, allocator); err = load_from_stream(stream, buf, allocator);
return err; return err;
} }
@@ -111,18 +111,16 @@ load_from_file :: proc(filename: string, buf: ^bytes.Buffer, allocator := contex
data, ok := os.read_entire_file(filename, allocator); data, ok := os.read_entire_file(filename, allocator);
defer delete(data); defer delete(data);
err = E_General.File_Not_Found;
if ok { if ok {
err = load_from_slice(&data, buf, allocator); err = load_from_slice(data, buf, allocator);
return;
} else {
return E_General.File_Not_Found;
} }
return;
} }
load_from_stream :: proc(stream: ^io.Stream, buf: ^bytes.Buffer, allocator := context.allocator) -> (err: Error) { load_from_stream :: proc(stream: io.Stream, buf: ^bytes.Buffer, allocator := context.allocator) -> (err: Error) {
ctx := compress.Context{ ctx := compress.Context{
input = stream^, input = stream,
}; };
buf := buf; buf := buf;
ws := bytes.buffer_to_stream(buf); ws := bytes.buffer_to_stream(buf);
@@ -313,4 +311,4 @@ load_from_stream :: proc(stream: ^io.Stream, buf: ^bytes.Buffer, allocator := co
return E_General.OK; return E_General.OK;
} }
load :: proc{load_from_file, load_from_slice, load_from_stream}; load :: proc{load_from_file, load_from_slice, load_from_stream};
+11 -11
View File
@@ -7,30 +7,30 @@ import "core:fmt"
main :: proc() { main :: proc() {
ODIN_DEMO: []u8 = { ODIN_DEMO := []u8{
120, 156, 101, 144, 77, 110, 131, 48, 16, 133, 215, 204, 41, 158, 44, 120, 156, 101, 144, 77, 110, 131, 48, 16, 133, 215, 204, 41, 158, 44,
69, 73, 32, 148, 182, 75, 35, 14, 208, 125, 47, 96, 185, 195, 143, 69, 73, 32, 148, 182, 75, 35, 14, 208, 125, 47, 96, 185, 195, 143,
130, 13, 50, 38, 81, 84, 101, 213, 75, 116, 215, 43, 246, 8, 53, 130, 13, 50, 38, 81, 84, 101, 213, 75, 116, 215, 43, 246, 8, 53,
82, 126, 8, 181, 188, 152, 153, 111, 222, 147, 159, 123, 165, 247, 170, 82, 126, 8, 181, 188, 152, 153, 111, 222, 147, 159, 123, 165, 247, 170,
98, 24, 213, 88, 162, 198, 244, 157, 243, 16, 186, 115, 44, 75, 227, 98, 24, 213, 88, 162, 198, 244, 157, 243, 16, 186, 115, 44, 75, 227,
5, 77, 115, 72, 137, 222, 117, 122, 179, 197, 39, 69, 161, 170, 156, 5, 77, 115, 72, 137, 222, 117, 122, 179, 197, 39, 69, 161, 170, 156,
50, 144, 5, 68, 130, 4, 49, 126, 127, 190, 191, 144, 34, 19, 57, 50, 144, 5, 68, 130, 4, 49, 126, 127, 190, 191, 144, 34, 19, 57,
69, 74, 235, 209, 140, 173, 242, 157, 155, 54, 158, 115, 162, 168, 12, 69, 74, 235, 209, 140, 173, 242, 157, 155, 54, 158, 115, 162, 168, 12,
181, 239, 246, 108, 17, 188, 174, 242, 224, 20, 13, 199, 198, 235, 250, 181, 239, 246, 108, 17, 188, 174, 242, 224, 20, 13, 199, 198, 235, 250,
194, 166, 129, 86, 3, 99, 157, 172, 37, 230, 62, 73, 129, 151, 252, 194, 166, 129, 86, 3, 99, 157, 172, 37, 230, 62, 73, 129, 151, 252,
70, 211, 5, 77, 31, 104, 188, 160, 113, 129, 215, 59, 205, 22, 52, 70, 211, 5, 77, 31, 104, 188, 160, 113, 129, 215, 59, 205, 22, 52,
123, 160, 83, 142, 255, 242, 89, 123, 93, 149, 200, 50, 188, 85, 54, 123, 160, 83, 142, 255, 242, 89, 123, 93, 149, 200, 50, 188, 85, 54,
252, 18, 248, 192, 238, 228, 235, 198, 86, 224, 118, 224, 176, 113, 166, 252, 18, 248, 192, 238, 228, 235, 198, 86, 224, 118, 224, 176, 113, 166,
112, 67, 106, 227, 159, 122, 215, 88, 95, 110, 196, 123, 205, 183, 224, 112, 67, 106, 227, 159, 122, 215, 88, 95, 110, 196, 123, 205, 183, 224,
98, 53, 8, 104, 213, 234, 201, 147, 7, 248, 192, 14, 170, 29, 25, 98, 53, 8, 104, 213, 234, 201, 147, 7, 248, 192, 14, 170, 29, 25,
171, 15, 18, 59, 138, 112, 63, 23, 205, 110, 254, 136, 109, 78, 231, 171, 15, 18, 59, 138, 112, 63, 23, 205, 110, 254, 136, 109, 78, 231,
63, 234, 138, 133, 204, 63, 234, 138, 133, 204,
}; };
buf: bytes.Buffer; buf: bytes.Buffer;
// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one. // We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
err := zlib.inflate(&ODIN_DEMO, &buf); err := zlib.inflate(ODIN_DEMO, &buf);
defer bytes.buffer_destroy(&buf); defer bytes.buffer_destroy(&buf);
if !zlib.is_kind(err, zlib.E_General.OK) { if !zlib.is_kind(err, zlib.E_General.OK) {
@@ -39,4 +39,4 @@ main :: proc() {
s := bytes.buffer_to_string(&buf); s := bytes.buffer_to_string(&buf);
fmt.printf("Input: %v bytes, output (%v bytes):\n%v\n", len(ODIN_DEMO), len(s), s); fmt.printf("Input: %v bytes, output (%v bytes):\n%v\n", len(ODIN_DEMO), len(s), s);
assert(len(s) == 438); assert(len(s) == 438);
} }
+65 -64
View File
@@ -21,8 +21,8 @@ Compression_Method :: enum u8 {
Compression_Level :: enum u8 { Compression_Level :: enum u8 {
Fastest = 0, Fastest = 0,
Fast = 1, Fast = 1,
Default = 2, Default = 2,
Maximum = 3, Maximum = 3,
} }
Options :: struct { Options :: struct {
@@ -68,19 +68,19 @@ Z_LENGTH_DEZIGZAG := []u8{
}; };
Z_FIXED_LENGTH := [288]u8{ Z_FIXED_LENGTH := [288]u8{
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,
}; };
Z_FIXED_DIST := [32]u8{ Z_FIXED_DIST := [32]u8{
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
}; };
/* /*
@@ -94,12 +94,12 @@ ZFAST_MASK :: ((1 << ZFAST_BITS) - 1);
JPEG packs from left, ZLIB from right. We can't share code. JPEG packs from left, ZLIB from right. We can't share code.
*/ */
Huffman_Table :: struct { Huffman_Table :: struct {
fast: [1 << ZFAST_BITS]u16, fast: [1 << ZFAST_BITS]u16,
firstcode: [16]u16, firstcode: [16]u16,
maxcode: [17]int, maxcode: [17]int,
firstsymbol: [16]u16, firstsymbol: [16]u16,
size: [288]u8, size: [288]u8,
value: [288]u16, value: [288]u16,
}; };
// Implementation starts here // Implementation starts here
@@ -218,19 +218,19 @@ decode_huffman_slowpath :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err:
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:
b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s]); b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s]);
if b >= size_of(t.size) { if b >= size_of(t.size) {
return 0, E_Deflate.Bad_Huffman_Code; return 0, E_Deflate.Bad_Huffman_Code;
} }
if t.size[b] != s { if t.size[b] != s {
return 0, E_Deflate.Bad_Huffman_Code; return 0, E_Deflate.Bad_Huffman_Code;
} }
compress.consume_bits_lsb(z, s); compress.consume_bits_lsb(z, s);
r = t.value[b]; r = t.value[b];
return r, E_General.OK; return r, E_General.OK;
} }
decode_huffman :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check { decode_huffman :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
@@ -254,7 +254,6 @@ decode_huffman :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: Error) #
} }
parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check { parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check {
#no_bounds_check for { #no_bounds_check for {
value, e := decode_huffman(z, z_repeat); value, e := decode_huffman(z, z_repeat);
if !is_kind(e, E_General.OK) { if !is_kind(e, E_General.OK) {
@@ -267,8 +266,8 @@ parse_huffman_block :: proc(z: ^Context, z_repeat, z_offset: ^Huffman_Table) ->
} }
} else { } else {
if value == 256 { if value == 256 {
// End of block // End of block
return E_General.OK; return E_General.OK;
} }
value -= 257; value -= 257;
@@ -370,7 +369,7 @@ inflate_from_stream :: proc(using ctx: ^Context, raw := false, allocator := cont
} }
fdict := (flg >> 5) & 1; fdict := (flg >> 5) & 1;
/* /*
We don't handle built-in dictionaries for now. We don't handle built-in dictionaries for now.
They're application specific and PNG doesn't use them. They're application specific and PNG doesn't use them.
*/ */
@@ -449,7 +448,8 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) ->
// log.debugf("Final: %v | Type: %v\n", final, type); // log.debugf("Final: %v | Type: %v\n", final, type);
if type == 0 { switch type {
case 0:
// Uncompressed block // Uncompressed block
// Discard bits until next byte boundary // Discard bits until next byte boundary
@@ -471,9 +471,9 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) ->
write_byte(z, u8(lit)); write_byte(z, u8(lit));
uncompressed_len -= 1; uncompressed_len -= 1;
} }
} else if type == 3 { case 3:
return E_Deflate.BType_3; return E_Deflate.BType_3;
} else { case:
// log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type); // log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type);
if type == 1 { if type == 1 {
// Use fixed code lengths. // Use fixed code lengths.
@@ -487,12 +487,12 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) ->
} }
} else { } else {
lencodes: [286+32+137]u8; lencodes: [286+32+137]u8;
codelength_sizes: [19]u8; codelength_sizes: [19]u8;
//i: u32; //i: u32;
n: u32; n: u32;
compress.refill_lsb(z, 14); compress.refill_lsb(z, 14);
hlit := compress.read_bits_no_refill_lsb(z, 5) + 257; hlit := compress.read_bits_no_refill_lsb(z, 5) + 257;
hdist := compress.read_bits_no_refill_lsb(z, 5) + 1; hdist := compress.read_bits_no_refill_lsb(z, 5) + 1;
hclen := compress.read_bits_no_refill_lsb(z, 4) + 4; hclen := compress.read_bits_no_refill_lsb(z, 4) + 4;
@@ -525,34 +525,35 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) ->
} else { } else {
fill := u8(0); fill := u8(0);
compress.refill_lsb(z, 7); compress.refill_lsb(z, 7);
if c == 16 { switch c {
case 16:
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3); c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3);
if n == 0 { if n == 0 {
return E_Deflate.Huffman_Bad_Code_Lengths; return E_Deflate.Huffman_Bad_Code_Lengths;
} }
fill = lencodes[n - 1]; fill = lencodes[n - 1];
} else if c == 17 { case 17:
c = u16(compress.read_bits_no_refill_lsb(z, 3) + 3); c = u16(compress.read_bits_no_refill_lsb(z, 3) + 3);
} else if c == 18 { case 18:
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11); c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11);
} else { case:
return E_Deflate.Huffman_Bad_Code_Lengths; return E_Deflate.Huffman_Bad_Code_Lengths;
} }
if ntot - n < u32(c) { if ntot - n < u32(c) {
return E_Deflate.Huffman_Bad_Code_Lengths; return E_Deflate.Huffman_Bad_Code_Lengths;
} }
nc := n + u32(c); nc := n + u32(c);
#no_bounds_check for ; n < nc; n += 1 { #no_bounds_check for ; n < nc; n += 1 {
lencodes[n] = fill; lencodes[n] = fill;
} }
} }
} }
if n != ntot { if n != ntot {
return E_Deflate.Huffman_Bad_Code_Lengths; return E_Deflate.Huffman_Bad_Code_Lengths;
} }
err = build_huffman(z_repeat, lencodes[:hlit]); err = build_huffman(z_repeat, lencodes[:hlit]);
if !is_kind(err, E_General.OK) { if !is_kind(err, E_General.OK) {
@@ -577,11 +578,11 @@ inflate_from_stream_raw :: proc(z: ^Context, allocator := context.allocator) ->
return E_General.OK; return E_General.OK;
} }
inflate_from_byte_array :: proc(input: ^[]u8, buf: ^bytes.Buffer, raw := false) -> (err: Error) { inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false) -> (err: Error) {
ctx := Context{}; ctx := Context{};
r := bytes.Reader{}; r := bytes.Reader{};
bytes.reader_init(&r, input^); bytes.reader_init(&r, input);
rs := bytes.reader_to_stream(&r); rs := bytes.reader_to_stream(&r);
ctx.input = rs; ctx.input = rs;
@@ -594,9 +595,9 @@ inflate_from_byte_array :: proc(input: ^[]u8, buf: ^bytes.Buffer, raw := false)
return err; return err;
} }
inflate_from_byte_array_raw :: proc(input: ^[]u8, buf: ^bytes.Buffer, raw := false) -> (err: Error) { inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := false) -> (err: Error) {
return inflate_from_byte_array(input, buf, true); return inflate_from_byte_array(input, buf, true);
} }
inflate :: proc{inflate_from_stream, inflate_from_byte_array}; inflate :: proc{inflate_from_stream, inflate_from_byte_array};
inflate_raw :: proc{inflate_from_stream_raw, inflate_from_byte_array_raw}; inflate_raw :: proc{inflate_from_stream_raw, inflate_from_byte_array_raw};
+2 -2
View File
@@ -194,7 +194,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
} }
defer close(fd); defer close(fd);
write_string(fd, write_string(fd,
fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << depth -1)), fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << depth -1)),
); );
@@ -324,4 +324,4 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
} }
} }
return true; return true;
} }
+89 -94
View File
@@ -76,102 +76,102 @@ core_time :: proc(c: Chunk) -> (t: coretime.Time, ok: bool) {
using png_time; using png_time;
return coretime.datetime_to_time( return coretime.datetime_to_time(
int(year), int(month), int(day), int(year), int(month), int(day),
int(hour), int(minute), int(second)); int(hour), int(minute), int(second),
);
} }
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;
fields := bytes.split(s=c.data, sep=[]u8{0}, allocator=context.temp_allocator); fields := bytes.split(s=c.data, sep=[]u8{0}, allocator=context.temp_allocator);
if len(fields) == 2 { if len(fields) == 2 {
res.keyword = strings.clone(string(fields[0])); res.keyword = strings.clone(string(fields[0]));
res.text = strings.clone(string(fields[1])); res.text = strings.clone(string(fields[1]));
} else { } else {
ok = false; ok = false;
} }
return; return;
case .zTXt: case .zTXt:
ok = true; ok = true;
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator); fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator);
if len(fields) != 3 || len(fields[1]) != 0 { if len(fields) != 3 || len(fields[1]) != 0 {
// Compression method must be 0=Deflate, which thanks to the split above turns // Compression method must be 0=Deflate, which thanks to the split above turns
// into an empty slice // into an empty slice
ok = false; return; ok = false; return;
} }
// Set up ZLIB context and decompress text payload.
buf: bytes.Buffer;
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf);
defer bytes.buffer_destroy(&buf);
if !is_kind(zlib_error, E_General.OK) {
ok = false; return;
}
res.keyword = strings.clone(string(fields[0]));
res.text = strings.clone(bytes.buffer_to_string(&buf));
return;
case .iTXt:
ok = true;
s := string(c.data);
null := strings.index_byte(s, 0);
if null == -1 {
ok = false; return;
}
if len(c.data) < null + 4 {
// At a minimum, including the \0 following the keyword, we require 5 more bytes.
ok = false; return;
}
res.keyword = strings.clone(string(c.data[:null]));
rest := c.data[null+1:];
compression_flag := rest[:1][0];
if compression_flag > 1 {
ok = false; return;
}
compression_method := rest[1:2][0];
if compression_flag == 1 && compression_method > 0 {
// Only Deflate is supported
ok = false; return;
}
rest = rest[2:];
// We now expect an optional language keyword and translated keyword, both followed by a \0
null = strings.index_byte(string(rest), 0);
if null == -1 {
ok = false; return;
}
res.language = strings.clone(string(rest[:null]));
rest = rest[null+1:];
null = strings.index_byte(string(rest), 0);
if null == -1 {
ok = false; return;
}
res.keyword_localized = strings.clone(string(rest[:null]));
rest = rest[null+1:];
if compression_flag == 0 {
res.text = strings.clone(string(rest));
} else {
// Set up ZLIB context and decompress text payload. // Set up ZLIB context and decompress text payload.
buf: bytes.Buffer; buf: bytes.Buffer;
zlib_error := zlib.inflate_from_byte_array(&fields[2], &buf); zlib_error := zlib.inflate_from_byte_array(rest, &buf);
defer bytes.buffer_destroy(&buf); defer bytes.buffer_destroy(&buf);
if !is_kind(zlib_error, E_General.OK) { if !is_kind(zlib_error, E_General.OK) {
ok = false; return; ok = false; return;
} }
res.keyword = strings.clone(string(fields[0]));
res.text = strings.clone(bytes.buffer_to_string(&buf)); res.text = strings.clone(bytes.buffer_to_string(&buf));
return; }
case .iTXt: return;
ok = true; case:
// PNG text helper called with an unrecognized chunk type.
s := string(c.data); ok = false; return;
null := strings.index_byte(s, 0);
if null == -1 {
ok = false; return;
}
if len(c.data) < null + 4 {
// At a minimum, including the \0 following the keyword, we require 5 more bytes.
ok = false; return;
}
res.keyword = strings.clone(string(c.data[:null]));
rest := c.data[null+1:];
compression_flag := rest[:1][0];
if compression_flag > 1 {
ok = false; return;
}
compression_method := rest[1:2][0];
if compression_flag == 1 && compression_method > 0 {
// Only Deflate is supported
ok = false; return;
}
rest = rest[2:];
// We now expect an optional language keyword and translated keyword, both followed by a \0
null = strings.index_byte(string(rest), 0);
if null == -1 {
ok = false; return;
}
res.language = strings.clone(string(rest[:null]));
rest = rest[null+1:];
null = strings.index_byte(string(rest), 0);
if null == -1 {
ok = false; return;
}
res.keyword_localized = strings.clone(string(rest[:null]));
rest = rest[null+1:];
if compression_flag == 0 {
res.text = strings.clone(string(rest));
} else {
// Set up ZLIB context and decompress text payload.
buf: bytes.Buffer;
zlib_error := zlib.inflate_from_byte_array(&rest, &buf);
defer bytes.buffer_destroy(&buf);
if !is_kind(zlib_error, E_General.OK) {
ok = false; return;
}
res.text = strings.clone(bytes.buffer_to_string(&buf));
}
return;
case:
// PNG text helper called with an unrecognized chunk type.
ok = false; return;
} }
} }
@@ -199,7 +199,7 @@ iccp :: proc(c: Chunk) -> (res: iCCP, ok: bool) {
// Set up ZLIB context and decompress iCCP payload // Set up ZLIB context and decompress iCCP payload
buf: bytes.Buffer; buf: bytes.Buffer;
zlib_error := zlib.inflate_from_byte_array(&fields[2], &buf); zlib_error := zlib.inflate_from_byte_array(fields[2], &buf);
if !is_kind(zlib_error, E_General.OK) { if !is_kind(zlib_error, E_General.OK) {
bytes.buffer_destroy(&buf); bytes.buffer_destroy(&buf);
ok = false; return; ok = false; return;
@@ -458,19 +458,14 @@ when false {
interlace_method = .None, interlace_method = .None,
}; };
if channels == 1 { switch channels {
ihdr.color_type = Color_Type{}; case 1: ihdr.color_type = Color_Type{};
} else if channels == 2 { case 2: ihdr.color_type = Color_Type{.Alpha};
ihdr.color_type = Color_Type{.Alpha}; case 3: ihdr.color_type = Color_Type{.Color};
} else if channels == 3 { case 4: ihdr.color_type = Color_Type{.Color, .Alpha};
ihdr.color_type = Color_Type{.Color}; case:// Unhandled
} else if channels == 4 {
ihdr.color_type = Color_Type{.Color, .Alpha};
} else {
// Unhandled
return E_PNG.Unknown_Color_Type; return E_PNG.Unknown_Color_Type;
} }
h := make_chunk(ihdr, .IHDR); h := make_chunk(ihdr, .IHDR);
write_chunk(fd, h); write_chunk(fd, h);
@@ -518,4 +513,4 @@ when false {
return E_General.OK; return E_General.OK;
} }
} }
+375 -374
View File
@@ -350,9 +350,9 @@ chunk_type_to_name :: proc(type: ^Chunk_Type) -> string {
return strings.string_from_ptr(t, 4); return strings.string_from_ptr(t, 4);
} }
load_from_slice :: proc(slice: ^[]u8, options: Options = {}, allocator := context.allocator) -> (img: ^Image, err: Error) { load_from_slice :: proc(slice: []u8, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
r := bytes.Reader{}; r := bytes.Reader{};
bytes.reader_init(&r, slice^); bytes.reader_init(&r, slice);
stream := bytes.reader_to_stream(&r); stream := bytes.reader_to_stream(&r);
/* /*
@@ -360,17 +360,17 @@ load_from_slice :: proc(slice: ^[]u8, options: Options = {}, allocator := contex
This way the stream reader could avoid the copy into the temp memory returned by it, This way the stream reader could avoid the copy into the temp memory returned by it,
and instead return a slice into the original memory that's already owned by the caller. and instead return a slice into the original memory that's already owned by the caller.
*/ */
img, err = load_from_stream(&stream, options, allocator); img, err = load_from_stream(stream, options, allocator);
return img, err; return img, err;
} }
load_from_file :: proc(filename: string, options: Options = {}, allocator := context.allocator) -> (img: ^Image, err: Error) { load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
data, ok := os.read_entire_file(filename, allocator); data, ok := os.read_entire_file(filename, allocator);
defer delete(data); defer delete(data);
if ok { if ok {
img, err = load_from_slice(&data, options, allocator); img, err = load_from_slice(data, options, allocator);
return; return;
} else { } else {
img = new(Image); img = new(Image);
@@ -378,7 +378,7 @@ load_from_file :: proc(filename: string, options: Options = {}, allocator := con
} }
} }
load_from_stream :: proc(stream: ^io.Stream, options: Options = {}, allocator := context.allocator) -> (img: ^Image, err: Error) { load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
options := options; options := options;
if .info in options { if .info in options {
options |= {.return_metadata, .do_not_decompress_image}; options |= {.return_metadata, .do_not_decompress_image};
@@ -396,7 +396,7 @@ load_from_stream :: proc(stream: ^io.Stream, options: Options = {}, allocator :=
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);
@@ -669,7 +669,7 @@ load_from_stream :: proc(stream: ^io.Stream, options: Options = {}, allocator :=
} }
buf: bytes.Buffer; buf: bytes.Buffer;
zlib_error := zlib.inflate(&idat, &buf); zlib_error := zlib.inflate(idat, &buf);
defer bytes.buffer_destroy(&buf); defer bytes.buffer_destroy(&buf);
if !is_kind(zlib_error, E_General.OK) { if !is_kind(zlib_error, E_General.OK) {
@@ -817,8 +817,7 @@ load_from_stream :: proc(stream: ^io.Stream, options: Options = {}, allocator :=
} }
} }
} else { } else {
// This should be impossible. unreachable();
assert(false);
} }
img.pixels = t; img.pixels = t;
@@ -845,145 +844,145 @@ load_from_stream :: proc(stream: ^io.Stream, options: Options = {}, allocator :=
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);
if seen_trns {
key = mem.slice_data_cast([]u16, trns.data)[0];
}
for len(p16) > 0 {
r := p16[0];
alpha := u16(1); // Default to full opaque
if seen_trns { if seen_trns {
key = mem.slice_data_cast([]u16, trns.data)[0]; if r == key {
} if seen_bkgd {
c := img.background.([3]u16);
for len(p16) > 0 { r = c[0];
r := p16[0]; } else {
alpha = 0; // Keyed transparency
alpha := u16(1); // Default to full opaque
if seen_trns {
if r == key {
if seen_bkgd {
c := img.background.([3]u16);
r = c[0];
} else {
alpha = 0; // Keyed transparency
}
} }
} }
if premultiply {
o16[0] = r * alpha;
o16[1] = r * alpha;
o16[2] = r * alpha;
} else {
o16[0] = r;
o16[1] = r;
o16[2] = r;
}
if out_image_channels == 4 {
o16[3] = alpha * 65535;
}
p16 = p16[1:];
o16 = o16[out_image_channels:];
} }
case 2:
// Gray with alpha, we shouldn't have a tRNS chunk.
for len(p16) > 0 {
r := p16[0];
if premultiply {
alpha := p16[1];
c := u16(f32(r) * f32(alpha) / f32(65535));
o16[0] = c;
o16[1] = c;
o16[2] = c;
} else {
o16[0] = r;
o16[1] = r;
o16[2] = r;
}
if .alpha_drop_if_present not_in options { if premultiply {
o16[3] = p16[1]; o16[0] = r * alpha;
} o16[1] = r * alpha;
o16[2] = r * alpha;
p16 = p16[2:]; } else {
o16 = o16[out_image_channels:]; o16[0] = r;
o16[1] = r;
o16[2] = r;
} }
case 3:
/*
Color without Alpha.
We may still have a tRNS chunk or `.alpha_add_if_missing`.
*/
key: []u16; if out_image_channels == 4 {
o16[3] = alpha * 65535;
}
p16 = p16[1:];
o16 = o16[out_image_channels:];
}
case 2:
// Gray with alpha, we shouldn't have a tRNS chunk.
for len(p16) > 0 {
r := p16[0];
if premultiply {
alpha := p16[1];
c := u16(f32(r) * f32(alpha) / f32(65535));
o16[0] = c;
o16[1] = c;
o16[2] = c;
} else {
o16[0] = r;
o16[1] = r;
o16[2] = r;
}
if .alpha_drop_if_present not_in options {
o16[3] = p16[1];
}
p16 = p16[2:];
o16 = o16[out_image_channels:];
}
case 3:
/*
Color without Alpha.
We may still have a tRNS chunk or `.alpha_add_if_missing`.
*/
key: []u16;
if seen_trns {
key = mem.slice_data_cast([]u16, trns.data);
}
for len(p16) > 0 {
r := p16[0];
g := p16[1];
b := p16[2];
alpha := u16(1); // Default to full opaque
if seen_trns { if seen_trns {
key = mem.slice_data_cast([]u16, trns.data); if r == key[0] && g == key[1] && b == key[2] {
} if seen_bkgd {
c := img.background.([3]u16);
for len(p16) > 0 { r = c[0];
r := p16[0]; g = c[1];
g := p16[1]; b = c[2];
b := p16[2]; } else {
alpha = 0; // Keyed transparency
alpha := u16(1); // Default to full opaque
if seen_trns {
if r == key[0] && g == key[1] && b == key[2] {
if seen_bkgd {
c := img.background.([3]u16);
r = c[0];
g = c[1];
b = c[2];
} else {
alpha = 0; // Keyed transparency
}
} }
} }
if premultiply {
o16[0] = r * alpha;
o16[1] = g * alpha;
o16[2] = b * alpha;
} else {
o16[0] = r;
o16[1] = g;
o16[2] = b;
}
if out_image_channels == 4 {
o16[3] = alpha * 65535;
}
p16 = p16[3:];
o16 = o16[out_image_channels:];
} }
case 4:
// Color with Alpha, can't have tRNS.
for len(p16) > 0 {
r := p16[0];
g := p16[1];
b := p16[2];
a := p16[3];
if premultiply { if premultiply {
alpha := f32(a) / 65535.0; o16[0] = r * alpha;
o16[0] = u16(f32(r) * alpha); o16[1] = g * alpha;
o16[1] = u16(f32(g) * alpha); o16[2] = b * alpha;
o16[2] = u16(f32(b) * alpha); } else {
} else { o16[0] = r;
o16[0] = r; o16[1] = g;
o16[1] = g; o16[2] = b;
o16[2] = b;
}
if .alpha_drop_if_present not_in options {
o16[3] = a;
}
p16 = p16[4:];
o16 = o16[out_image_channels:];
} }
case:
unreachable("We should never seen # channels other than 1-4 inclusive."); if out_image_channels == 4 {
o16[3] = alpha * 65535;
}
p16 = p16[3:];
o16 = o16[out_image_channels:];
}
case 4:
// Color with Alpha, can't have tRNS.
for len(p16) > 0 {
r := p16[0];
g := p16[1];
b := p16[2];
a := p16[3];
if premultiply {
alpha := f32(a) / 65535.0;
o16[0] = u16(f32(r) * alpha);
o16[1] = u16(f32(g) * alpha);
o16[2] = u16(f32(b) * alpha);
} else {
o16[0] = r;
o16[1] = g;
o16[2] = b;
}
if .alpha_drop_if_present not_in options {
o16[3] = a;
}
p16 = p16[4:];
o16 = o16[out_image_channels:];
}
case:
unreachable("We should never seen # channels other than 1-4 inclusive.");
} }
img.pixels = t; img.pixels = t;
@@ -1011,143 +1010,143 @@ load_from_stream :: proc(stream: ^io.Stream, options: Options = {}, allocator :=
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);
if seen_trns {
key = u8(mem.slice_data_cast([]u16be, trns.data)[0]);
}
for len(p) > 0 {
r := p[0];
alpha := u8(1);
if seen_trns { if seen_trns {
key = u8(mem.slice_data_cast([]u16be, trns.data)[0]); if r == key {
} if seen_bkgd {
c := img.background.([3]u16);
for len(p) > 0 { r = u8(c[0]);
r := p[0]; } else {
alpha := u8(1); alpha = 0; // Keyed transparency
if seen_trns {
if r == key {
if seen_bkgd {
c := img.background.([3]u16);
r = u8(c[0]);
} else {
alpha = 0; // Keyed transparency
}
} }
if premultiply {
o[0] = r * alpha;
o[1] = r * alpha;
o[2] = r * alpha;
}
} else {
o[0] = r;
o[1] = r;
o[2] = r;
} }
if premultiply {
if out_image_channels == 4 { o[0] = r * alpha;
o[3] = alpha * 255; o[1] = r * alpha;
o[2] = r * alpha;
} }
} else {
p = p[1:]; o[0] = r;
o = o[out_image_channels:]; o[1] = r;
o[2] = r;
} }
case 2:
// Gray with alpha, we shouldn't have a tRNS chunk.
for len(p) > 0 {
r := p[0];
if .alpha_premultiply in options {
alpha := p[1];
c := u8(f32(r) * f32(alpha) / f32(255));
o[0] = c;
o[1] = c;
o[2] = c;
} else {
o[0] = r;
o[1] = r;
o[2] = r;
}
if .alpha_drop_if_present not_in options { if out_image_channels == 4 {
o[3] = p[1]; o[3] = alpha * 255;
}
p = p[2:];
o = o[out_image_channels:];
} }
case 3:
// Color without Alpha. We may still have a tRNS chunk p = p[1:];
key: []u8; o = o[out_image_channels:];
}
case 2:
// Gray with alpha, we shouldn't have a tRNS chunk.
for len(p) > 0 {
r := p[0];
if .alpha_premultiply in options {
alpha := p[1];
c := u8(f32(r) * f32(alpha) / f32(255));
o[0] = c;
o[1] = c;
o[2] = c;
} else {
o[0] = r;
o[1] = r;
o[2] = r;
}
if .alpha_drop_if_present not_in options {
o[3] = p[1];
}
p = p[2:];
o = o[out_image_channels:];
}
case 3:
// Color without Alpha. We may still have a tRNS chunk
key: []u8;
if seen_trns {
/*
For 8-bit images, the tRNS chunk still contains a triple in u16be.
We use only the low byte in this case.
*/
key = []u8{trns.data[1], trns.data[3], trns.data[5]};
}
for len(p) > 0 {
r := p[0];
g := p[1];
b := p[2];
alpha := u8(1); // Default to full opaque
// TODO: Combine the seen_trns cases.
if seen_trns { if seen_trns {
/* if r == key[0] && g == key[1] && b == key[2] {
For 8-bit images, the tRNS chunk still contains a triple in u16be. if seen_bkgd {
We use only the low byte in this case. c := img.background.([3]u16);
*/ r = u8(c[0]);
key = []u8{trns.data[1], trns.data[3], trns.data[5]}; g = u8(c[1]);
} b = u8(c[2]);
for len(p) > 0 { } else {
r := p[0]; alpha = 0; // Keyed transparency
g := p[1];
b := p[2];
alpha := u8(1); // Default to full opaque
// TODO: Combine the seen_trns cases.
if seen_trns {
if r == key[0] && g == key[1] && b == key[2] {
if seen_bkgd {
c := img.background.([3]u16);
r = u8(c[0]);
g = u8(c[1]);
b = u8(c[2]);
} else {
alpha = 0; // Keyed transparency
}
} }
if .alpha_premultiply in options || .blend_background in options {
o[0] = r * alpha;
o[1] = g * alpha;
o[2] = b * alpha;
}
} else {
o[0] = r;
o[1] = g;
o[2] = b;
} }
if out_image_channels == 4 { if .alpha_premultiply in options || .blend_background in options {
o[3] = alpha * 255; o[0] = r * alpha;
o[1] = g * alpha;
o[2] = b * alpha;
} }
} else {
p = p[3:]; o[0] = r;
o = o[out_image_channels:]; o[1] = g;
o[2] = b;
} }
case 4:
// Color with Alpha, can't have tRNS.
for len(p) > 0 {
r := p[0];
g := p[1];
b := p[2];
a := p[3];
if .alpha_premultiply in options { if out_image_channels == 4 {
alpha := f32(a) / 255.0; o[3] = alpha * 255;
o[0] = u8(f32(r) * alpha);
o[1] = u8(f32(g) * alpha);
o[2] = u8(f32(b) * alpha);
} else {
o[0] = r;
o[1] = g;
o[2] = b;
}
if .alpha_drop_if_present not_in options {
o[3] = a;
}
p = p[4:];
o = o[out_image_channels:];
} }
case:
unreachable("We should never seen # channels other than 1-4 inclusive."); p = p[3:];
o = o[out_image_channels:];
}
case 4:
// Color with Alpha, can't have tRNS.
for len(p) > 0 {
r := p[0];
g := p[1];
b := p[2];
a := p[3];
if .alpha_premultiply in options {
alpha := f32(a) / 255.0;
o[0] = u8(f32(r) * alpha);
o[1] = u8(f32(g) * alpha);
o[2] = u8(f32(b) * alpha);
} else {
o[0] = r;
o[1] = g;
o[2] = b;
}
if .alpha_drop_if_present not_in options {
o[3] = a;
}
p = p[4:];
o = o[out_image_channels:];
}
case:
unreachable("We should never seen # channels other than 1-4 inclusive.");
} }
img.pixels = t; img.pixels = t;
@@ -1181,13 +1180,13 @@ filter_paeth :: #force_inline proc(left, up, up_left: u8) -> u8 {
} }
Filter_Params :: struct #packed { Filter_Params :: struct #packed {
src : []u8, src: []u8,
dest : []u8, dest: []u8,
width : int, width: int,
height : int, height: int,
depth : int, depth: int,
channels: int, channels: int,
rescale : bool, rescale: bool,
} }
depth_scale_table :: []u8{0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01}; depth_scale_table :: []u8{0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01};
@@ -1210,39 +1209,39 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) {
filter := Row_Filter(src[0]); src = src[1:]; filter := Row_Filter(src[0]); src = src[1:];
// fmt.printf("Row: %v | Filter: %v\n", y, filter); // fmt.printf("Row: %v | Filter: %v\n", y, filter);
switch(filter) { switch(filter) {
case .None: case .None:
copy(dest, src[:row_stride]); copy(dest, src[:row_stride]);
case .Sub: case .Sub:
for i := 0; i < channels; i += 1 { for i := 0; i < channels; i += 1 {
dest[i] = src[i]; dest[i] = src[i];
} }
for k := 0; k < nk; k += 1 { for k := 0; k < nk; k += 1 {
dest[channels+k] = (src[channels+k] + dest[k]) & 255; dest[channels+k] = (src[channels+k] + dest[k]) & 255;
} }
case .Up: case .Up:
for k := 0; k < row_stride; k += 1 { for k := 0; k < row_stride; k += 1 {
dest[k] = (src[k] + up[k]) & 255; dest[k] = (src[k] + up[k]) & 255;
} }
case .Average: case .Average:
for i := 0; i < channels; i += 1 { for i := 0; i < channels; i += 1 {
avg := up[i] >> 1; avg := up[i] >> 1;
dest[i] = (src[i] + avg) & 255; dest[i] = (src[i] + avg) & 255;
} }
for k := 0; k < nk; k += 1 { for k := 0; k < nk; k += 1 {
avg := u8((u16(up[channels+k]) + u16(dest[k])) >> 1); avg := u8((u16(up[channels+k]) + u16(dest[k])) >> 1);
dest[channels+k] = (src[channels+k] + avg) & 255; dest[channels+k] = (src[channels+k] + avg) & 255;
} }
case .Paeth: case .Paeth:
for i := 0; i < channels; i += 1 { for i := 0; i < channels; i += 1 {
paeth := filter_paeth(0, up[i], 0); paeth := filter_paeth(0, up[i], 0);
dest[i] = (src[i] + paeth) & 255; dest[i] = (src[i] + paeth) & 255;
} }
for k := 0; k < nk; k += 1 { for k := 0; k < nk; k += 1 {
paeth := filter_paeth(dest[k], up[channels+k], up[k]); paeth := filter_paeth(dest[k], up[channels+k], up[k]);
dest[channels+k] = (src[channels+k] + paeth) & 255; dest[channels+k] = (src[channels+k] + paeth) & 255;
} }
case: case:
return false; return false;
} }
src = src[row_stride:]; src = src[row_stride:];
@@ -1277,45 +1276,45 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch
dest = dest[row_offset:]; dest = dest[row_offset:];
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_in]); copy(dest, src[:row_stride_in]);
case .Sub: case .Sub:
for i in 0..channels { for i in 0..channels {
dest[i] = src[i]; dest[i] = src[i];
} }
for k in 0..nk { for k in 0..nk {
dest[channels+k] = (src[channels+k] + dest[k]) & 255; dest[channels+k] = (src[channels+k] + dest[k]) & 255;
} }
case .Up: case .Up:
for k in 0..row_stride_in { for k in 0..row_stride_in {
dest[k] = (src[k] + up[k]) & 255; dest[k] = (src[k] + up[k]) & 255;
} }
case .Average: case .Average:
for i in 0..channels { for i in 0..channels {
avg := up[i] >> 1; avg := up[i] >> 1;
dest[i] = (src[i] + avg) & 255; dest[i] = (src[i] + avg) & 255;
} }
for k in 0..nk { for k in 0..nk {
avg := u8((u16(up[channels+k]) + u16(dest[k])) >> 1); avg := u8((u16(up[channels+k]) + u16(dest[k])) >> 1);
dest[channels+k] = (src[channels+k] + avg) & 255; dest[channels+k] = (src[channels+k] + avg) & 255;
} }
case .Paeth: case .Paeth:
for i in 0..channels { for i in 0..channels {
paeth := filter_paeth(0, up[i], 0); paeth := filter_paeth(0, up[i], 0);
dest[i] = (src[i] + paeth) & 255; dest[i] = (src[i] + paeth) & 255;
} }
for k in 0..nk { for k in 0..nk {
paeth := filter_paeth(dest[k], up[channels], up[k]); paeth := filter_paeth(dest[k], up[channels], up[k]);
dest[channels+k] = (src[channels+k] + paeth) & 255; dest[channels+k] = (src[channels+k] + paeth) & 255;
} }
case: case:
return false; return false;
} }
src = src [row_stride_in:]; src = src [row_stride_in:];
up = dest; up = dest;
dest = dest[row_stride_in:]; dest = dest[row_stride_in:];
} }
// Let's expand the bits // Let's expand the bits
@@ -1334,7 +1333,8 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch
for j := 0; j < height; j += 1 { for j := 0; j < height; j += 1 {
src = dest[row_offset:]; src = dest[row_offset:];
if depth == 4 { switch depth {
case 4:
k := row_stride_out; k := row_stride_out;
for ; k >= 2; k -= 2 { for ; k >= 2; k -= 2 {
c := src[0]; c := src[0];
@@ -1347,7 +1347,7 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch
dest[0] = scale * (c >> 4); dest[0] = scale * (c >> 4);
dest = dest[1:]; dest = dest[1:];
} }
} else if depth == 2 { case 2:
k := row_stride_out; k := row_stride_out;
for ; k >= 4; k -= 4 { for ; k >= 4; k -= 4 {
c := src[0]; c := src[0];
@@ -1368,7 +1368,7 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch
} }
dest = dest[k:]; dest = dest[k:];
} }
} else if depth == 1 { case 1:
k := row_stride_out; k := row_stride_out;
for ; k >= 8; k -= 8 { for ; k >= 8; k -= 8 {
c := src[0]; c := src[0];
@@ -1406,6 +1406,7 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch
dest = dest[k:]; dest = dest[k:];
} }
} }
} }
@@ -1429,40 +1430,40 @@ defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) {
nk := row_stride - stride; nk := row_stride - stride;
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:
for i := 0; i < stride; i += 1 { for i := 0; i < stride; i += 1 {
dest[i] = src[i]; dest[i] = src[i];
} }
for k := 0; k < nk; k += 1 { for k := 0; k < nk; k += 1 {
dest[stride+k] = (src[stride+k] + dest[k]) & 255; dest[stride+k] = (src[stride+k] + dest[k]) & 255;
} }
case .Up: case .Up:
for k := 0; k < row_stride; k += 1 { for k := 0; k < row_stride; k += 1 {
dest[k] = (src[k] + up[k]) & 255; dest[k] = (src[k] + up[k]) & 255;
} }
case .Average: case .Average:
for i := 0; i < stride; i += 1 { for i := 0; i < stride; i += 1 {
avg := up[i] >> 1; avg := up[i] >> 1;
dest[i] = (src[i] + avg) & 255; dest[i] = (src[i] + avg) & 255;
} }
for k := 0; k < nk; k += 1 { for k := 0; k < nk; k += 1 {
avg := u8((u16(up[stride+k]) + u16(dest[k])) >> 1); avg := u8((u16(up[stride+k]) + u16(dest[k])) >> 1);
dest[stride+k] = (src[stride+k] + avg) & 255; dest[stride+k] = (src[stride+k] + avg) & 255;
} }
case .Paeth: case .Paeth:
for i := 0; i < stride; i += 1 { for i := 0; i < stride; i += 1 {
paeth := filter_paeth(0, up[i], 0); paeth := filter_paeth(0, up[i], 0);
dest[i] = (src[i] + paeth) & 255; dest[i] = (src[i] + paeth) & 255;
} }
for k := 0; k < nk; k += 1 { for k := 0; k < nk; k += 1 {
paeth := filter_paeth(dest[k], up[stride+k], up[k]); paeth := filter_paeth(dest[k], up[stride+k], up[k]);
dest[stride+k] = (src[stride+k] + paeth) & 255; dest[stride+k] = (src[stride+k] + paeth) & 255;
} }
case: case:
return false; return false;
} }
src = src[row_stride:]; src = src[row_stride:];
@@ -1510,7 +1511,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option
if !filter_ok { if !filter_ok {
// Caller will destroy buffer for us. // Caller will destroy buffer for us.
return E_PNG.Unknown_Filter_Method; return E_PNG.Unknown_Filter_Method;
} }
} else { } else {
/* /*
For deinterlacing we need to make a temporary buffer, defiilter part of the image, For deinterlacing we need to make a temporary buffer, defiilter part of the image,
@@ -1582,7 +1583,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option
} }
} }
return E_General.OK; return E_General.OK;
} }
load :: proc{load_from_file, load_from_slice, load_from_stream}; load :: proc{load_from_file, load_from_slice, load_from_stream};