From c4b4a841d68f4bde69f4e1472b7d8fa24f96a376 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 6 Oct 2021 22:43:33 +0200 Subject: [PATCH] png: Move metadata. --- core/image/common.odin | 105 +++++++++++++++- core/image/png/example.odin | 166 +++++++++++++------------- core/image/png/helpers.odin | 41 +++---- core/image/png/png.odin | 124 +++---------------- tests/core/image/test_core_image.odin | 4 +- 5 files changed, 218 insertions(+), 222 deletions(-) diff --git a/core/image/common.odin b/core/image/common.odin index 2826a65ca..919670a61 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -26,8 +26,11 @@ Image :: struct { */ background: Maybe([3]u16), - metadata_ptr: rawptr, - metadata_type: typeid, + metadata: Image_Metadata, +} + +Image_Metadata :: union { + ^PNG_Info, } /* @@ -152,10 +155,101 @@ PNG_Error :: enum { Invalid_Chunk_Length, } +/* + PNG-specific structs +*/ +PNG_Info :: struct { + header: PNG_IHDR, + chunks: [dynamic]PNG_Chunk, +} + +PNG_Chunk_Header :: struct #packed { + length: u32be, + type: PNG_Chunk_Type, +} + +PNG_Chunk :: struct #packed { + header: PNG_Chunk_Header, + data: []byte, + crc: u32be, +} + +PNG_Chunk_Type :: enum u32be { + // IHDR must come first in a file + IHDR = 'I' << 24 | 'H' << 16 | 'D' << 8 | 'R', + // PLTE must precede the first IDAT chunk + PLTE = 'P' << 24 | 'L' << 16 | 'T' << 8 | 'E', + bKGD = 'b' << 24 | 'K' << 16 | 'G' << 8 | 'D', + tRNS = 't' << 24 | 'R' << 16 | 'N' << 8 | 'S', + IDAT = 'I' << 24 | 'D' << 16 | 'A' << 8 | 'T', + + iTXt = 'i' << 24 | 'T' << 16 | 'X' << 8 | 't', + tEXt = 't' << 24 | 'E' << 16 | 'X' << 8 | 't', + zTXt = 'z' << 24 | 'T' << 16 | 'X' << 8 | 't', + + iCCP = 'i' << 24 | 'C' << 16 | 'C' << 8 | 'P', + pHYs = 'p' << 24 | 'H' << 16 | 'Y' << 8 | 's', + gAMA = 'g' << 24 | 'A' << 16 | 'M' << 8 | 'A', + tIME = 't' << 24 | 'I' << 16 | 'M' << 8 | 'E', + + sPLT = 's' << 24 | 'P' << 16 | 'L' << 8 | 'T', + sRGB = 's' << 24 | 'R' << 16 | 'G' << 8 | 'B', + hIST = 'h' << 24 | 'I' << 16 | 'S' << 8 | 'T', + cHRM = 'c' << 24 | 'H' << 16 | 'R' << 8 | 'M', + sBIT = 's' << 24 | 'B' << 16 | 'I' << 8 | 'T', + + /* + eXIf tags are not part of the core spec, but have been ratified + in v1.5.0 of the PNG Ext register. + + We will provide unprocessed chunks to the caller if `.return_metadata` is set. + Applications are free to implement an Exif decoder. + */ + eXIf = 'e' << 24 | 'X' << 16 | 'I' << 8 | 'f', + + // PNG files must end with IEND + IEND = 'I' << 24 | 'E' << 16 | 'N' << 8 | 'D', + + /* + XCode sometimes produces "PNG" files that don't adhere to the PNG spec. + We recognize them only in order to avoid doing further work on them. + + Some tools like PNG Defry may be able to repair them, but we're not + going to reward Apple for producing proprietary broken files purporting + to be PNGs by supporting them. + + */ + iDOT = 'i' << 24 | 'D' << 16 | 'O' << 8 | 'T', + CbGI = 'C' << 24 | 'b' << 16 | 'H' << 8 | 'I', +} + +PNG_IHDR :: struct #packed { + width: u32be, + height: u32be, + bit_depth: u8, + color_type: PNG_Color_Type, + compression_method: u8, + filter_method: u8, + interlace_method: PNG_Interlace_Method, +} +PNG_IHDR_SIZE :: size_of(PNG_IHDR) +#assert (PNG_IHDR_SIZE == 13) + +PNG_Color_Value :: enum u8 { + Paletted = 0, // 1 << 0 = 1 + Color = 1, // 1 << 1 = 2 + Alpha = 2, // 1 << 2 = 4 +} +PNG_Color_Type :: distinct bit_set[PNG_Color_Value; u8] + +PNG_Interlace_Method :: enum u8 { + None = 0, + Adam7 = 1, +} + /* Functions to help with image buffer calculations */ - 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 @@ -164,7 +258,6 @@ compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes /* For when you have an RGB(A) image, but want a particular channel. */ - Channel :: enum u8 { R = 1, G = 2, @@ -226,8 +319,8 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok res.depth = img.depth res.pixels = t res.background = img.background - res.metadata_ptr = img.metadata_ptr - res.metadata_type = img.metadata_type + // res.metadata_ptr = img.metadata_ptr + // res.metadata_type = img.metadata_type return res, true } diff --git a/core/image/png/example.odin b/core/image/png/example.odin index 5370b0bcf..5e7dca4c8 100644 --- a/core/image/png/example.odin +++ b/core/image/png/example.odin @@ -53,93 +53,91 @@ demo :: proc() { } else { fmt.printf("Image: %vx%vx%v, %v-bit.\n", img.width, img.height, img.channels, img.depth) - assert(img.metadata_ptr != nil && img.metadata_type == Info) - - v := (^Info)(img.metadata_ptr) - - // 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: - if t, t_ok := core_time(c); t_ok { - fmt.printf("[tIME]: %v\n", t) - } - case .gAMA: - if gama, gama_ok := gamma(c); gama_ok { - fmt.printf("[gAMA]: %v\n", gama) - } - case .pHYs: - if phys, phys_ok := phys(c); phys_ok { - if phys.unit == .Meter { - xm := f32(img.width) / f32(phys.ppu_x) - ym := f32(img.height) / f32(phys.ppu_y) - dpi_x, dpi_y := 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) + if v, ok := img.metadata.(^image.PNG_Info); ok { + // 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: + if t, t_ok := core_time(c); t_ok { + fmt.printf("[tIME]: %v\n", t) } - } - case .iTXt, .zTXt, .tEXt: - res, ok_text := 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) + case .gAMA: + if gama, gama_ok := gamma(c); gama_ok { + fmt.printf("[gAMA]: %v\n", gama) } + case .pHYs: + if phys, phys_ok := phys(c); phys_ok { + if phys.unit == .Meter { + xm := f32(img.width) / f32(phys.ppu_x) + ym := f32(img.height) / f32(phys.ppu_y) + dpi_x, dpi_y := 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 := 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 text_destroy(res) + case .bKGD: + fmt.printf("[bKGD] %v\n", img.background) + case .eXIf: + if res, ok_exif := exif(c); 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: + if plte, plte_ok := plte(c); plte_ok { + fmt.printf("[PLTE] %v\n", plte) + } else { + fmt.printf("[PLTE] Error\n") + } + case .hIST: + if res, ok_hist := hist(c); ok_hist { + fmt.printf("[hIST] %v\n", res) + } + case .cHRM: + if res, ok_chrm := chrm(c); ok_chrm { + fmt.printf("[cHRM] %v\n", res) + } + case .sPLT: + res, ok_splt := splt(c) + if ok_splt { + fmt.printf("[sPLT] %v\n", res) + } + splt_destroy(res) + case .sBIT: + if res, ok_sbit := sbit(c); ok_sbit { + fmt.printf("[sBIT] %v\n", res) + } + case .iCCP: + res, ok_iccp := iccp(c) + if ok_iccp { + fmt.printf("[iCCP] %v\n", res) + } + iccp_destroy(res) + case .sRGB: + if res, ok_srgb := srgb(c); ok_srgb { + fmt.printf("[sRGB] Rendering intent: %v\n", res) + } + case: + type := c.header.type + name := chunk_type_to_name(&type) + fmt.printf("[%v]: %v\n", name, c.data) } - defer text_destroy(res) - case .bKGD: - fmt.printf("[bKGD] %v\n", img.background) - case .eXIf: - if res, ok_exif := exif(c); 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: - if plte, plte_ok := plte(c); plte_ok { - fmt.printf("[PLTE] %v\n", plte) - } else { - fmt.printf("[PLTE] Error\n") - } - case .hIST: - if res, ok_hist := hist(c); ok_hist { - fmt.printf("[hIST] %v\n", res) - } - case .cHRM: - if res, ok_chrm := chrm(c); ok_chrm { - fmt.printf("[cHRM] %v\n", res) - } - case .sPLT: - res, ok_splt := splt(c) - if ok_splt { - fmt.printf("[sPLT] %v\n", res) - } - splt_destroy(res) - case .sBIT: - if res, ok_sbit := sbit(c); ok_sbit { - fmt.printf("[sBIT] %v\n", res) - } - case .iCCP: - res, ok_iccp := iccp(c) - if ok_iccp { - fmt.printf("[iCCP] %v\n", res) - } - iccp_destroy(res) - case .sRGB: - if res, ok_srgb := srgb(c); ok_srgb { - fmt.printf("[sRGB] Rendering intent: %v\n", res) - } - case: - type := c.header.type - name := 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 59d8fb70b..ecc0183bc 100644 --- a/core/image/png/helpers.odin +++ b/core/image/png/helpers.odin @@ -34,16 +34,13 @@ destroy :: proc(img: ^Image) { bytes.buffer_destroy(&img.pixels) - assert(img.metadata_ptr != nil && img.metadata_type == Info) - v := (^Info)(img.metadata_ptr) - - for chunk in &v.chunks { - delete(chunk.data) + if v, ok := img.metadata.(^image.PNG_Info); ok { + for chunk in &v.chunks { + delete(chunk.data) + } + delete(v.chunks) + free(v) } - delete(v.chunks) - - // Clean up Info. - free(img.metadata_ptr) free(img) } @@ -51,7 +48,7 @@ destroy :: proc(img: ^Image) { Chunk helpers */ -gamma :: proc(c: Chunk) -> (res: f32, ok: bool) { +gamma :: proc(c: image.PNG_Chunk) -> (res: f32, ok: bool) { if c.header.type != .gAMA || len(c.data) != size_of(gAMA) { return {}, false } @@ -61,7 +58,7 @@ gamma :: proc(c: Chunk) -> (res: f32, ok: bool) { INCHES_PER_METER :: 1000.0 / 25.4 -phys :: proc(c: Chunk) -> (res: pHYs, ok: bool) { +phys :: proc(c: image.PNG_Chunk) -> (res: pHYs, ok: bool) { if c.header.type != .pHYs || len(c.data) != size_of(pHYs) { return {}, false } @@ -73,7 +70,7 @@ phys_to_dpi :: proc(p: pHYs) -> (x_dpi, y_dpi: f32) { return f32(p.ppu_x) / INCHES_PER_METER, f32(p.ppu_y) / INCHES_PER_METER } -time :: proc(c: Chunk) -> (res: tIME, ok: bool) { +time :: proc(c: image.PNG_Chunk) -> (res: tIME, ok: bool) { if c.header.type != .tIME || len(c.data) != size_of(tIME) { return {}, false } @@ -81,7 +78,7 @@ time :: proc(c: Chunk) -> (res: tIME, ok: bool) { return (^tIME)(raw_data(c.data))^, true } -core_time :: proc(c: Chunk) -> (t: coretime.Time, ok: bool) { +core_time :: proc(c: image.PNG_Chunk) -> (t: coretime.Time, ok: bool) { if png_time, png_ok := time(c); png_ok { using png_time return coretime.datetime_to_time( @@ -93,7 +90,7 @@ core_time :: proc(c: Chunk) -> (t: coretime.Time, ok: bool) { } } -text :: proc(c: Chunk) -> (res: Text, ok: bool) { +text :: proc(c: image.PNG_Chunk) -> (res: Text, ok: bool) { assert(len(c.data) == int(c.header.length)) #partial switch c.header.type { case .tEXt: @@ -196,7 +193,7 @@ text_destroy :: proc(text: Text) { delete(text.text) } -iccp :: proc(c: Chunk) -> (res: iCCP, ok: bool) { +iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) { ok = true fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator) @@ -232,7 +229,7 @@ iccp_destroy :: proc(i: iCCP) { } -srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) { +srgb :: proc(c: image.PNG_Chunk) -> (res: sRGB, ok: bool) { if c.header.type != .sRGB || len(c.data) != size_of(sRGB_Rendering_Intent) { return {}, false } @@ -244,7 +241,7 @@ srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) { return res, true } -plte :: proc(c: Chunk) -> (res: PLTE, ok: bool) { +plte :: proc(c: image.PNG_Chunk) -> (res: PLTE, ok: bool) { if c.header.type != .PLTE { return {}, false } @@ -258,7 +255,7 @@ plte :: proc(c: Chunk) -> (res: PLTE, ok: bool) { return } -splt :: proc(c: Chunk) -> (res: sPLT, ok: bool) { +splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) { if c.header.type != .sPLT { return {}, false } @@ -309,7 +306,7 @@ splt_destroy :: proc(s: sPLT) { delete(s.name) } -sbit :: proc(c: Chunk) -> (res: [4]u8, ok: bool) { +sbit :: proc(c: image.PNG_Chunk) -> (res: [4]u8, ok: bool) { /* Returns [4]u8 with the significant bits in each channel. A channel will contain zero if not applicable to the PNG color type. @@ -327,7 +324,7 @@ sbit :: proc(c: Chunk) -> (res: [4]u8, ok: bool) { } -hist :: proc(c: Chunk) -> (res: hIST, ok: bool) { +hist :: proc(c: image.PNG_Chunk) -> (res: hIST, ok: bool) { if c.header.type != .hIST { return {}, false } @@ -349,7 +346,7 @@ hist :: proc(c: Chunk) -> (res: hIST, ok: bool) { return } -chrm :: proc(c: Chunk) -> (res: cHRM, ok: bool) { +chrm :: proc(c: image.PNG_Chunk) -> (res: cHRM, ok: bool) { ok = true if c.header.length != size_of(cHRM_Raw) { return {}, false @@ -367,7 +364,7 @@ chrm :: proc(c: Chunk) -> (res: cHRM, ok: bool) { return } -exif :: proc(c: Chunk) -> (res: Exif, ok: bool) { +exif :: proc(c: image.PNG_Chunk) -> (res: Exif, ok: bool) { ok = true diff --git a/core/image/png/png.odin b/core/image/png/png.odin index 536b82be2..f77bf7519 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -51,95 +51,6 @@ Signature :: enum u64be { PNG = 0x89 << 56 | 'P' << 48 | 'N' << 40 | 'G' << 32 | '\r' << 24 | '\n' << 16 | 0x1a << 8 | '\n', } -Info :: struct { - header: IHDR, - chunks: [dynamic]Chunk, -} - -Chunk_Header :: struct #packed { - length: u32be, - type: Chunk_Type, -} - -Chunk :: struct #packed { - header: Chunk_Header, - data: []byte, - crc: u32be, -} - -Chunk_Type :: enum u32be { - // IHDR must come first in a file - IHDR = 'I' << 24 | 'H' << 16 | 'D' << 8 | 'R', - // PLTE must precede the first IDAT chunk - PLTE = 'P' << 24 | 'L' << 16 | 'T' << 8 | 'E', - bKGD = 'b' << 24 | 'K' << 16 | 'G' << 8 | 'D', - tRNS = 't' << 24 | 'R' << 16 | 'N' << 8 | 'S', - IDAT = 'I' << 24 | 'D' << 16 | 'A' << 8 | 'T', - - iTXt = 'i' << 24 | 'T' << 16 | 'X' << 8 | 't', - tEXt = 't' << 24 | 'E' << 16 | 'X' << 8 | 't', - zTXt = 'z' << 24 | 'T' << 16 | 'X' << 8 | 't', - - iCCP = 'i' << 24 | 'C' << 16 | 'C' << 8 | 'P', - pHYs = 'p' << 24 | 'H' << 16 | 'Y' << 8 | 's', - gAMA = 'g' << 24 | 'A' << 16 | 'M' << 8 | 'A', - tIME = 't' << 24 | 'I' << 16 | 'M' << 8 | 'E', - - sPLT = 's' << 24 | 'P' << 16 | 'L' << 8 | 'T', - sRGB = 's' << 24 | 'R' << 16 | 'G' << 8 | 'B', - hIST = 'h' << 24 | 'I' << 16 | 'S' << 8 | 'T', - cHRM = 'c' << 24 | 'H' << 16 | 'R' << 8 | 'M', - sBIT = 's' << 24 | 'B' << 16 | 'I' << 8 | 'T', - - /* - eXIf tags are not part of the core spec, but have been ratified - in v1.5.0 of the PNG Ext register. - - We will provide unprocessed chunks to the caller if `.return_metadata` is set. - Applications are free to implement an Exif decoder. - */ - eXIf = 'e' << 24 | 'X' << 16 | 'I' << 8 | 'f', - - // PNG files must end with IEND - IEND = 'I' << 24 | 'E' << 16 | 'N' << 8 | 'D', - - /* - XCode sometimes produces "PNG" files that don't adhere to the PNG spec. - We recognize them only in order to avoid doing further work on them. - - Some tools like PNG Defry may be able to repair them, but we're not - going to reward Apple for producing proprietary broken files purporting - to be PNGs by supporting them. - - */ - iDOT = 'i' << 24 | 'D' << 16 | 'O' << 8 | 'T', - CbGI = 'C' << 24 | 'b' << 16 | 'H' << 8 | 'I', -} - -IHDR :: struct #packed { - width: u32be, - height: u32be, - bit_depth: u8, - color_type: Color_Type, - compression_method: u8, - filter_method: u8, - interlace_method: Interlace_Method, -} -IHDR_SIZE :: size_of(IHDR) -#assert (IHDR_SIZE == 13) - -Color_Value :: enum u8 { - Paletted = 0, // 1 << 0 = 1 - Color = 1, // 1 << 1 = 2 - Alpha = 2, // 1 << 2 = 4 -} -Color_Type :: distinct bit_set[Color_Value; u8] - -Interlace_Method :: enum u8 { - None = 0, - Adam7 = 1, -} - Row_Filter :: enum u8 { None = 0, Sub = 1, @@ -262,8 +173,8 @@ ADAM7_Y_SPACING := []int{ 8,8,8,4,4,2,2 } // Implementation starts here -read_chunk :: proc(ctx: ^$C) -> (chunk: Chunk, err: Error) { - ch, e := compress.read_data(ctx, Chunk_Header) +read_chunk :: proc(ctx: ^$C) -> (chunk: image.PNG_Chunk, err: Error) { + ch, e := compress.read_data(ctx, image.PNG_Chunk_Header) if e != .None { return {}, compress.General_Error.Stream_Too_Short } @@ -305,7 +216,7 @@ read_chunk :: proc(ctx: ^$C) -> (chunk: Chunk, err: Error) { return chunk, nil } -copy_chunk :: proc(src: Chunk, allocator := context.allocator) -> (dest: Chunk, err: Error) { +copy_chunk :: proc(src: image.PNG_Chunk, allocator := context.allocator) -> (dest: image.PNG_Chunk, err: Error) { if int(src.header.length) != len(src.data) { return {}, .Invalid_Chunk_Length } @@ -318,7 +229,7 @@ copy_chunk :: proc(src: Chunk, allocator := context.allocator) -> (dest: Chunk, return } -append_chunk :: proc(list: ^[dynamic]Chunk, src: Chunk, allocator := context.allocator) -> (err: Error) { +append_chunk :: proc(list: ^[dynamic]image.PNG_Chunk, src: image.PNG_Chunk, allocator := context.allocator) -> (err: Error) { if int(src.header.length) != len(src.data) { return .Invalid_Chunk_Length } @@ -334,13 +245,13 @@ append_chunk :: proc(list: ^[dynamic]Chunk, src: Chunk, allocator := context.all return } -read_header :: proc(ctx: ^$C) -> (IHDR, Error) { +read_header :: proc(ctx: ^$C) -> (image.PNG_IHDR, Error) { c, e := read_chunk(ctx) if e != nil { return {}, e } - header := (^IHDR)(raw_data(c.data))^ + header := (^image.PNG_IHDR)(raw_data(c.data))^ // Validate IHDR using header if width == 0 || height == 0 || u128(width) * u128(height) > MAX_DIMENSIONS { @@ -407,7 +318,7 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) { return header, nil } -chunk_type_to_name :: proc(type: ^Chunk_Type) -> string { +chunk_type_to_name :: proc(type: ^image.PNG_Chunk_Type) -> string { t := transmute(^u8)type return strings.string_from_ptr(t, 4) } @@ -462,9 +373,8 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a img = new(Image) } - info := new(Info) - img.metadata_ptr = info - img.metadata_type = typeid_of(Info) + info := new(image.PNG_Info) + img.metadata = info signature, io_error := compress.read_data(ctx, Signature) if io_error != .None || signature != .PNG { @@ -477,11 +387,11 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a idat_length := u64(0) - c: Chunk - ch: Chunk_Header + c: image.PNG_Chunk + ch: image.PNG_Chunk_Header e: io.Error - header: IHDR + header: image.PNG_IHDR // State to ensure correct chunk ordering. seen_ihdr := false; first := true @@ -492,7 +402,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a seen_iend := false _plte := PLTE{} - trns := Chunk{} + trns := image.PNG_Chunk{} final_image_channels := 0 @@ -502,7 +412,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a // 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, image.PNG_Chunk_Header) if e != .None { return img, compress.General_Error.Stream_Too_Short } @@ -547,7 +457,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a img.height = int(header.height) using header - h := IHDR{ + h := image.PNG_IHDR{ width = width, height = height, bit_depth = bit_depth, @@ -607,7 +517,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a return {}, image.PNG_Error.IDAT_Size_Too_Large } - ch, e = compress.peek_data(ctx, Chunk_Header) + ch, e = compress.peek_data(ctx, image.PNG_Chunk_Header) if e != .None { return img, compress.General_Error.Stream_Too_Short } @@ -1599,7 +1509,7 @@ defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) { return } -defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, options: Options) -> (err: Error) { +defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IHDR, options: Options) -> (err: Error) { input := bytes.buffer_to_bytes(filter_bytes) width := int(header.width) height := int(header.height) diff --git a/tests/core/image/test_core_image.odin b/tests/core/image/test_core_image.odin index 7752cf7dc..155b69298 100644 --- a/tests/core/image/test_core_image.odin +++ b/tests/core/image/test_core_image.odin @@ -1504,10 +1504,8 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) { passed &= test.hash == hash if .return_metadata in test.options { - v: ^png.Info - if img.metadata_ptr != nil && img.metadata_type == png.Info { - v = (^png.Info)(img.metadata_ptr) + if v, ok := img.metadata.(^image.PNG_Info); ok { for c in v.chunks { #partial switch(c.header.type) { case .gAMA: