diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin index 779122c1e..d0e99d820 100644 --- a/core/compress/zlib/zlib.odin +++ b/core/compress/zlib/zlib.odin @@ -145,7 +145,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) { mem.zero_slice(sizes[:]); mem.zero_slice(z.fast[:]); - for v, _ in code_lengths { + for v in code_lengths { sizes[v] += 1; } sizes[0] = 0; @@ -163,7 +163,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) { z.firstsymbol[i] = u16(k); code = code + sizes[i]; if sizes[i] != 0 { - if (code - 1 >= (1 << u16(i))) { + if code - 1 >= (1 << u16(i)) { 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)); z.size[c] = u8(v); z.value[c] = u16(ci); - if (v <= ZFAST_BITS) { + if v <= ZFAST_BITS { j := z_bit_reverse(u16(next_code[v]), v); for j < (1 << ZFAST_BITS) { 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 { - r = 0; - err = nil; - - k: int; - s: u8; - 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; ; { if k < t.maxcode[s] { @@ -211,7 +206,7 @@ decode_huffman_slowpath :: proc(z: ^Context, t: ^Huffman_Table) -> (r: u16, err: } s += 1; } - if (s >= 16) { + if s >= 16 { return 0, E_Deflate.Bad_Huffman_Code; } // code size is s, so: diff --git a/core/image/common.odin b/core/image/common.odin index 9024ec769..9d46b81e6 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -127,7 +127,6 @@ Error :: enum { */ 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; return; } @@ -144,7 +143,6 @@ Channel :: enum u8 { } return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) { - ok = false; t: bytes.Buffer; @@ -159,36 +157,36 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok return {}, false; } - switch(img.depth) { - case 8: - buffer_size := compute_buffer_size(img.width, img.height, 1, 8); - t = bytes.Buffer{}; - resize(&t.buf, buffer_size); + switch img.depth { + case 8: + buffer_size := compute_buffer_size(img.width, img.height, 1, 8); + t = bytes.Buffer{}; + resize(&t.buf, buffer_size); - i := bytes.buffer_to_bytes(&img.pixels); - o := bytes.buffer_to_bytes(&t); + i := bytes.buffer_to_bytes(&img.pixels); + o := bytes.buffer_to_bytes(&t); - for len(i) > 0 { - o[0] = i[idx]; - i = i[img.channels:]; - o = o[1:]; - } - case 16: - buffer_size := compute_buffer_size(img.width, img.height, 2, 8); - t = bytes.Buffer{}; - resize(&t.buf, buffer_size); + for len(i) > 0 { + o[0] = i[idx]; + i = i[img.channels:]; + o = o[1:]; + } + case 16: + buffer_size := compute_buffer_size(img.width, img.height, 2, 8); + t = bytes.Buffer{}; + resize(&t.buf, buffer_size); - i := mem.slice_data_cast([]u16, img.pixels.buf[:]); - o := mem.slice_data_cast([]u16, t.buf[:]); + i := mem.slice_data_cast([]u16, img.pixels.buf[:]); + o := mem.slice_data_cast([]u16, t.buf[:]); - for len(i) > 0 { - o[0] = i[idx]; - i = i[img.channels:]; - o = o[1:]; - } - case 1, 2, 4: - // We shouldn't see this case, as the loader already turns these into 8-bit. - return {}, false; + for len(i) > 0 { + o[0] = i[idx]; + i = i[img.channels:]; + o = o[1:]; + } + case 1, 2, 4: + // We shouldn't see this case, as the loader already turns these into 8-bit. + return {}, false; } res = new(Image); diff --git a/core/image/png/example.odin b/core/image/png/example.odin index 59a4cfd42..30cbf5d21 100644 --- a/core/image/png/example.odin +++ b/core/image/png/example.odin @@ -35,86 +35,86 @@ main :: proc() { // Handle ancillary chunks as you wish. // We provide helper functions for a few types. for c in v.chunks { - #partial switch (c.header.type) { - case .tIME: - t, _ := png.core_time(c); - fmt.printf("[tIME]: %v\n", t); - case .gAMA: - fmt.printf("[gAMA]: %v\n", png.gamma(c)); - case .pHYs: - phys := png.phys(c); - if phys.unit == .Meter { - xm := f32(img.width) / f32(phys.ppu_x); - ym := f32(img.height) / f32(phys.ppu_y); - 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 DPI.\n", dpi_x, dpi_y); - fmt.printf("[pHYs] Image dimensions are %v x %v meters.\n", xm, ym); + #partial switch c.header.type { + case .tIME: + t, _ := png.core_time(c); + fmt.printf("[tIME]: %v\n", t); + case .gAMA: + fmt.printf("[gAMA]: %v\n", png.gamma(c)); + case .pHYs: + phys := png.phys(c); + if phys.unit == .Meter { + xm := f32(img.width) / f32(phys.ppu_x); + ym := f32(img.height) / f32(phys.ppu_y); + 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 DPI.\n", dpi_x, dpi_y); + 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 { - 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); - 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 { - fmt.printf("[tEXt/zTXt] %v: %v\n", res.keyword, res.text); - } - } - defer png.text_destroy(res); - case .bKGD: - fmt.printf("[bKGD] %v\n", img.background); - case .eXIf: - res, ok_exif := png.exif(c); - if ok_exif { - /* - Other than checking the signature and byte order, we don't handle Exif data. - If you wish to interpret it, pass it to an Exif parser. - */ - fmt.printf("[eXIf] %v\n", res); - } - case .PLTE: - plte, plte_ok := png.plte(c); - if plte_ok { - fmt.printf("[PLTE] %v\n", plte); - } else { - fmt.printf("[PLTE] Error\n"); - } - case .hIST: - res, ok_hist := png.hist(c); - if ok_hist { - fmt.printf("[hIST] %v\n", res); - } - case .cHRM: - res, ok_chrm := png.chrm(c); - if ok_chrm { - fmt.printf("[cHRM] %v\n", res); - } - case .sPLT: - res, ok_splt := png.splt(c); - if ok_splt { - fmt.printf("[sPLT] %v\n", res); - } - png.splt_destroy(res); - case .sBIT: - if res, ok_sbit := png.sbit(c); ok_sbit { - fmt.printf("[sBIT] %v\n", res); - } - case .iCCP: - res, ok_iccp := png.iccp(c); - if ok_iccp { - fmt.printf("[iCCP] %v\n", res); - } - png.iccp_destroy(res); - 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); + } + defer png.text_destroy(res); + case .bKGD: + fmt.printf("[bKGD] %v\n", img.background); + case .eXIf: + res, ok_exif := png.exif(c); + if ok_exif { + /* + Other than checking the signature and byte order, we don't handle Exif data. + If you wish to interpret it, pass it to an Exif parser. + */ + fmt.printf("[eXIf] %v\n", res); + } + case .PLTE: + plte, plte_ok := png.plte(c); + if plte_ok { + fmt.printf("[PLTE] %v\n", plte); + } else { + fmt.printf("[PLTE] Error\n"); + } + case .hIST: + res, ok_hist := png.hist(c); + if ok_hist { + fmt.printf("[hIST] %v\n", res); + } + case .cHRM: + res, ok_chrm := png.chrm(c); + if ok_chrm { + fmt.printf("[cHRM] %v\n", res); + } + case .sPLT: + res, ok_splt := png.splt(c); + if ok_splt { + fmt.printf("[sPLT] %v\n", res); + } + png.splt_destroy(res); + case .sBIT: + if res, ok_sbit := png.sbit(c); ok_sbit { + fmt.printf("[sBIT] %v\n", res); + } + case .iCCP: + res, ok_iccp := png.iccp(c); + if ok_iccp { + fmt.printf("[iCCP] %v\n", res); + } + png.iccp_destroy(res); + 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); } } } diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin index 0975d1d87..3a811f5c9 100644 --- a/core/image/png/helpers.odin +++ b/core/image/png/helpers.odin @@ -81,7 +81,7 @@ core_time :: proc(c: Chunk) -> (t: coretime.Time, ok: bool) { } text :: proc(c: Chunk) -> (res: Text, ok: bool) { - #partial switch c.header.type { + #partial switch c.header.type { case .tEXt: ok = true; diff --git a/core/image/png/png.odin b/core/image/png/png.odin index 642b30a26..4880f1f51 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -115,11 +115,11 @@ Interlace_Method :: enum u8 { } Row_Filter :: enum u8 { - None = 0, - Sub = 1, - Up = 2, - Average = 3, - Paeth = 4, + None = 0, + Sub = 1, + Up = 2, + Average = 3, + Paeth = 4, }; PLTE_Entry :: [3]u8; @@ -166,18 +166,18 @@ CIE_1931 :: struct #packed { } cHRM_Raw :: struct #packed { - w: CIE_1931_Raw, - r: CIE_1931_Raw, - g: CIE_1931_Raw, - b: CIE_1931_Raw, + w: CIE_1931_Raw, + r: CIE_1931_Raw, + g: CIE_1931_Raw, + b: CIE_1931_Raw, } #assert(size_of(cHRM_Raw) == 32); cHRM :: struct #packed { - w: CIE_1931, - r: CIE_1931, - g: CIE_1931, - b: CIE_1931, + w: CIE_1931, + r: CIE_1931, + g: CIE_1931, + b: CIE_1931, } #assert(size_of(cHRM) == 32); @@ -236,10 +236,7 @@ ADAM7_Y_SPACING := []int{ 8,8,8,4,4,2,2 }; // Implementation starts here -read_chunk :: proc(ctx: ^compress.Context) -> (Chunk, Error) { - - chunk := Chunk{}; - +read_chunk :: proc(ctx: ^compress.Context) -> (chunk: Chunk, err: Error) { ch, e := compress.read_data(ctx, Chunk_Header); if e != .None { 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) { - c, e := read_chunk(ctx); if e != nil { return {}, e; @@ -297,48 +293,48 @@ read_header :: proc(ctx: ^compress.Context) -> (IHDR, Error) { } - switch (transmute(u8)color_type) { - case 0: - /* - Grayscale. - Allowed bit depths: 1, 2, 4, 8 and 16. - */ - allowed := false; - for i in ([]u8{1, 2, 4, 8, 16}) { - if bit_depth == i { - allowed = true; - break; - } + switch transmute(u8)color_type { + case 0: + /* + Grayscale. + Allowed bit depths: 1, 2, 4, 8 and 16. + */ + allowed := false; + for i in ([]u8{1, 2, 4, 8, 16}) { + if bit_depth == i { + allowed = true; + break; } - if !allowed { - return {}, E_PNG.Invalid_Color_Bit_Depth_Combo; - } - case 2, 4, 6: - /* - 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; - } - case 3: - /* - Paletted. PLTE chunk must appear. - Allowed bit depths: 1, 2, 4 and 8. - */ - allowed := false; - for i in ([]u8{1, 2, 4, 8}) { - if bit_depth == i { - allowed = true; - 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: + /* + 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; + } + case 3: + /* + Paletted. PLTE chunk must appear. + Allowed bit depths: 1, 2, 4 and 8. + */ + allowed := false; + for i in ([]u8{1, 2, 4, 8}) { + if bit_depth == i { + allowed = true; + break; } + } + if !allowed { + return {}, E_PNG.Invalid_Color_Bit_Depth_Combo; + } - case: - return {}, E_PNG.Unknown_Color_Type; + case: + return {}, E_PNG.Unknown_Color_Type; } return header, nil; @@ -398,11 +394,11 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c img.sidecar = nil; - ctx := compress.Context{ + ctx := &compress.Context{ input = stream, }; - signature, io_error := compress.read_data(&ctx, Signature); + signature, io_error := compress.read_data(ctx, Signature); if io_error != .None || signature != .PNG { 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; // 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. // 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 { return img, E_General.Stream_Too_Short; } // name := chunk_type_to_name(&ch.type); // Only used for debug prints during development. #partial switch(ch.type) { - case .IHDR: - if seen_ihdr || !first { - return {}, E_PNG.IHDR_Not_First_Chunk; - } - seen_ihdr = true; + case .IHDR: + if seen_ihdr || !first { + return {}, E_PNG.IHDR_Not_First_Chunk; + } + seen_ihdr = true; - header, err = read_header(&ctx); - if err != nil { - return img, err; - } + header, err = read_header(ctx); + if err != nil { + return img, err; + } - if .Paletted in header.color_type { - // Color type 3 - img.channels = 1; - final_image_channels = 3; - img.depth = 8; - } else if .Color in header.color_type { - // Color image without a palette - img.channels = 3; - final_image_channels = 3; - img.depth = header.bit_depth; - } else { - // Grayscale - img.channels = 1; - final_image_channels = 1; - 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 .Paletted in header.color_type { + // Color type 3 + img.channels = 1; + final_image_channels = 3; + img.depth = 8; + } else if .Color in header.color_type { + // Color image without a palette + img.channels = 3; + final_image_channels = 3; + img.depth = header.bit_depth; + } else { + // Grayscale + img.channels = 1; + final_image_channels = 1; + img.depth = header.bit_depth; + } + if .Alpha in header.color_type { + img.channels += 1; 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 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; } - if .return_metadata in options { - // NOTE: Chunk cata is currently allocated on the temp allocator. - append(&info.chunks, c); + + 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'. + */ + + 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; } @@ -695,7 +691,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c for p := 0; p < 7; p += 1 { 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]; - 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); } } @@ -854,7 +850,7 @@ load_from_stream :: proc(stream: io.Stream, options := Options{}, allocator := c p16 := mem.slice_data_cast([]u16, temp.buf[:]); o16 := mem.slice_data_cast([]u16, t.buf[:]); - switch (raw_image_channels) { + switch raw_image_channels { case 1: // Gray without Alpha. Might have tRNS alpha. 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[:]); o := mem.slice_data_cast([]u8, t.buf[:]); - switch (raw_image_channels) { + switch raw_image_channels { case 1: // Gray without Alpha. Might have tRNS alpha. key := u8(0); @@ -1276,7 +1272,7 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) { nk := row_stride - channels; filter := Row_Filter(src[0]); src = src[1:]; - switch(filter) { + switch filter { case .None: copy(dest, src[:row_stride]); case .Sub: @@ -1590,7 +1586,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option i,j,x,y: int; 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]; - if (x > 0 && y > 0) { + if x > 0 && y > 0 { temp: bytes.Buffer; temp_len := compute_buffer_size(x, y, channels, depth == 16 ? 16 : 8); resize(&temp.buf, temp_len);