png: Move metadata.

This commit is contained in:
Jeroen van Rijn
2021-10-06 22:43:33 +02:00
parent 263d63aa56
commit c4b4a841d6
5 changed files with 218 additions and 222 deletions
+99 -6
View File
@@ -26,8 +26,11 @@ Image :: struct {
*/ */
background: Maybe([3]u16), background: Maybe([3]u16),
metadata_ptr: rawptr, metadata: Image_Metadata,
metadata_type: typeid, }
Image_Metadata :: union {
^PNG_Info,
} }
/* /*
@@ -152,10 +155,101 @@ PNG_Error :: enum {
Invalid_Chunk_Length, 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 Functions to help with image buffer calculations
*/ */
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
@@ -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. For when you have an RGB(A) image, but want a particular channel.
*/ */
Channel :: enum u8 { Channel :: enum u8 {
R = 1, R = 1,
G = 2, G = 2,
@@ -226,8 +319,8 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok
res.depth = img.depth res.depth = img.depth
res.pixels = t res.pixels = t
res.background = img.background res.background = img.background
res.metadata_ptr = img.metadata_ptr // res.metadata_ptr = img.metadata_ptr
res.metadata_type = img.metadata_type // res.metadata_type = img.metadata_type
return res, true return res, true
} }
+82 -84
View File
@@ -53,93 +53,91 @@ demo :: proc() {
} else { } else {
fmt.printf("Image: %vx%vx%v, %v-bit.\n", img.width, img.height, img.channels, img.depth) 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) if v, ok := img.metadata.(^image.PNG_Info); ok {
// Handle ancillary chunks as you wish.
v := (^Info)(img.metadata_ptr) // We provide helper functions for a few types.
for c in v.chunks {
// Handle ancillary chunks as you wish. #partial switch c.header.type {
// We provide helper functions for a few types. case .tIME:
for c in v.chunks { if t, t_ok := core_time(c); t_ok {
#partial switch c.header.type { fmt.printf("[tIME]: %v\n", t)
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)
} }
} case .gAMA:
case .iTXt, .zTXt, .tEXt: if gama, gama_ok := gamma(c); gama_ok {
res, ok_text := text(c) fmt.printf("[gAMA]: %v\n", gama)
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 .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)
} }
} }
} }
+19 -22
View File
@@ -34,16 +34,13 @@ destroy :: proc(img: ^Image) {
bytes.buffer_destroy(&img.pixels) bytes.buffer_destroy(&img.pixels)
assert(img.metadata_ptr != nil && img.metadata_type == Info) if v, ok := img.metadata.(^image.PNG_Info); ok {
v := (^Info)(img.metadata_ptr) for chunk in &v.chunks {
delete(chunk.data)
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) free(img)
} }
@@ -51,7 +48,7 @@ destroy :: proc(img: ^Image) {
Chunk helpers 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) { if c.header.type != .gAMA || len(c.data) != size_of(gAMA) {
return {}, false return {}, false
} }
@@ -61,7 +58,7 @@ gamma :: proc(c: Chunk) -> (res: f32, ok: bool) {
INCHES_PER_METER :: 1000.0 / 25.4 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) { if c.header.type != .pHYs || len(c.data) != size_of(pHYs) {
return {}, false 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 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) { if c.header.type != .tIME || len(c.data) != size_of(tIME) {
return {}, false return {}, false
} }
@@ -81,7 +78,7 @@ time :: proc(c: Chunk) -> (res: tIME, ok: bool) {
return (^tIME)(raw_data(c.data))^, true 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 { if png_time, png_ok := time(c); png_ok {
using png_time using png_time
return coretime.datetime_to_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)) assert(len(c.data) == int(c.header.length))
#partial switch c.header.type { #partial switch c.header.type {
case .tEXt: case .tEXt:
@@ -196,7 +193,7 @@ text_destroy :: proc(text: Text) {
delete(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 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)
@@ -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) { if c.header.type != .sRGB || len(c.data) != size_of(sRGB_Rendering_Intent) {
return {}, false return {}, false
} }
@@ -244,7 +241,7 @@ srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) {
return res, true 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 { if c.header.type != .PLTE {
return {}, false return {}, false
} }
@@ -258,7 +255,7 @@ plte :: proc(c: Chunk) -> (res: PLTE, ok: bool) {
return return
} }
splt :: proc(c: Chunk) -> (res: sPLT, ok: bool) { splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
if c.header.type != .sPLT { if c.header.type != .sPLT {
return {}, false return {}, false
} }
@@ -309,7 +306,7 @@ splt_destroy :: proc(s: sPLT) {
delete(s.name) 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. Returns [4]u8 with the significant bits in each channel.
A channel will contain zero if not applicable to the PNG color type. 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 { if c.header.type != .hIST {
return {}, false return {}, false
} }
@@ -349,7 +346,7 @@ hist :: proc(c: Chunk) -> (res: hIST, ok: bool) {
return return
} }
chrm :: proc(c: Chunk) -> (res: cHRM, ok: bool) { chrm :: proc(c: image.PNG_Chunk) -> (res: cHRM, ok: bool) {
ok = true ok = true
if c.header.length != size_of(cHRM_Raw) { if c.header.length != size_of(cHRM_Raw) {
return {}, false return {}, false
@@ -367,7 +364,7 @@ chrm :: proc(c: Chunk) -> (res: cHRM, ok: bool) {
return return
} }
exif :: proc(c: Chunk) -> (res: Exif, ok: bool) { exif :: proc(c: image.PNG_Chunk) -> (res: Exif, ok: bool) {
ok = true ok = true
+17 -107
View File
@@ -51,95 +51,6 @@ Signature :: enum u64be {
PNG = 0x89 << 56 | 'P' << 48 | 'N' << 40 | 'G' << 32 | '\r' << 24 | '\n' << 16 | 0x1a << 8 | '\n', 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 { Row_Filter :: enum u8 {
None = 0, None = 0,
Sub = 1, Sub = 1,
@@ -262,8 +173,8 @@ ADAM7_Y_SPACING := []int{ 8,8,8,4,4,2,2 }
// Implementation starts here // Implementation starts here
read_chunk :: proc(ctx: ^$C) -> (chunk: Chunk, err: Error) { read_chunk :: proc(ctx: ^$C) -> (chunk: image.PNG_Chunk, err: Error) {
ch, e := compress.read_data(ctx, Chunk_Header) ch, e := compress.read_data(ctx, image.PNG_Chunk_Header)
if e != .None { if e != .None {
return {}, compress.General_Error.Stream_Too_Short return {}, compress.General_Error.Stream_Too_Short
} }
@@ -305,7 +216,7 @@ read_chunk :: proc(ctx: ^$C) -> (chunk: Chunk, err: Error) {
return chunk, nil 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) { if int(src.header.length) != len(src.data) {
return {}, .Invalid_Chunk_Length return {}, .Invalid_Chunk_Length
} }
@@ -318,7 +229,7 @@ copy_chunk :: proc(src: Chunk, allocator := context.allocator) -> (dest: Chunk,
return 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) { if int(src.header.length) != len(src.data) {
return .Invalid_Chunk_Length return .Invalid_Chunk_Length
} }
@@ -334,13 +245,13 @@ append_chunk :: proc(list: ^[dynamic]Chunk, src: Chunk, allocator := context.all
return return
} }
read_header :: proc(ctx: ^$C) -> (IHDR, Error) { read_header :: proc(ctx: ^$C) -> (image.PNG_IHDR, Error) {
c, e := read_chunk(ctx) c, e := read_chunk(ctx)
if e != nil { if e != nil {
return {}, e return {}, e
} }
header := (^IHDR)(raw_data(c.data))^ header := (^image.PNG_IHDR)(raw_data(c.data))^
// Validate IHDR // Validate IHDR
using header using header
if width == 0 || height == 0 || u128(width) * u128(height) > MAX_DIMENSIONS { 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 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 t := transmute(^u8)type
return strings.string_from_ptr(t, 4) 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) img = new(Image)
} }
info := new(Info) info := new(image.PNG_Info)
img.metadata_ptr = info img.metadata = info
img.metadata_type = typeid_of(Info)
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 {
@@ -477,11 +387,11 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
idat_length := u64(0) idat_length := u64(0)
c: Chunk c: image.PNG_Chunk
ch: Chunk_Header ch: image.PNG_Chunk_Header
e: io.Error e: io.Error
header: IHDR header: image.PNG_IHDR
// State to ensure correct chunk ordering. // State to ensure correct chunk ordering.
seen_ihdr := false; first := true seen_ihdr := false; first := true
@@ -492,7 +402,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
seen_iend := false seen_iend := false
_plte := PLTE{} _plte := PLTE{}
trns := Chunk{} trns := image.PNG_Chunk{}
final_image_channels := 0 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. // 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, image.PNG_Chunk_Header)
if e != .None { if e != .None {
return img, compress.General_Error.Stream_Too_Short 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) img.height = int(header.height)
using header using header
h := IHDR{ h := image.PNG_IHDR{
width = width, width = width,
height = height, height = height,
bit_depth = bit_depth, 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 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 { if e != .None {
return img, compress.General_Error.Stream_Too_Short return img, compress.General_Error.Stream_Too_Short
} }
@@ -1599,7 +1509,7 @@ defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) {
return 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) input := bytes.buffer_to_bytes(filter_bytes)
width := int(header.width) width := int(header.width)
height := int(header.height) height := int(header.height)
+1 -3
View File
@@ -1504,10 +1504,8 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
passed &= test.hash == hash passed &= test.hash == hash
if .return_metadata in test.options { if .return_metadata in test.options {
v: ^png.Info
if img.metadata_ptr != nil && img.metadata_type == png.Info { if v, ok := img.metadata.(^image.PNG_Info); ok {
v = (^png.Info)(img.metadata_ptr)
for c in v.chunks { for c in v.chunks {
#partial switch(c.header.type) { #partial switch(c.header.type) {
case .gAMA: case .gAMA: