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
+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',
}
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)