png + compress: Rearrange error unions.

This commit is contained in:
Jeroen van Rijn
2021-10-06 20:45:29 +02:00
parent 21c6d691d8
commit 9b5ae95677
6 changed files with 78 additions and 68 deletions
+3 -9
View File
@@ -9,9 +9,8 @@ package compress
*/ */
import "core:io" import "core:io"
import "core:image"
import "core:bytes" import "core:bytes"
import "core:mem" import "core:runtime"
/* /*
These settings bound how much compression algorithms will allocate for their output buffer. These settings bound how much compression algorithms will allocate for their output buffer.
@@ -48,16 +47,12 @@ when size_of(uintptr) == 8 {
Error :: union { Error :: union {
General_Error, General_Error,
mem.Allocator_Error,
Deflate_Error, Deflate_Error,
ZLIB_Error, ZLIB_Error,
GZIP_Error, GZIP_Error,
ZIP_Error, ZIP_Error,
/*
This is here because png.load will return a this type of error union, runtime.Allocator_Error,
as it may involve an I/O error, a Deflate error, etc.
*/
image.Error,
} }
General_Error :: enum { General_Error :: enum {
@@ -71,7 +66,6 @@ General_Error :: enum {
Incompatible_Options, Incompatible_Options,
Unimplemented, Unimplemented,
/* /*
Memory errors Memory errors
*/ */
+20 -3
View File
@@ -11,6 +11,8 @@ package image
import "core:bytes" import "core:bytes"
import "core:mem" import "core:mem"
import "core:compress"
import "core:runtime"
Image :: struct { Image :: struct {
width: int, width: int,
@@ -112,19 +114,34 @@ Option :: enum {
} }
Options :: distinct bit_set[Option] Options :: distinct bit_set[Option]
Error :: enum { Error :: union {
General_Image_Error,
PNG_Error,
compress.Error,
compress.General_Error,
compress.Deflate_Error,
compress.ZLIB_Error,
runtime.Allocator_Error,
}
General_Image_Error :: enum {
None = 0,
Invalid_Image_Dimensions,
Image_Does_Not_Adhere_to_Spec,
}
PNG_Error :: enum {
Invalid_PNG_Signature, Invalid_PNG_Signature,
IHDR_Not_First_Chunk, IHDR_Not_First_Chunk,
IHDR_Corrupt, IHDR_Corrupt,
IDAT_Missing, IDAT_Missing,
IDAT_Must_Be_Contiguous, IDAT_Must_Be_Contiguous,
IDAT_Corrupt, IDAT_Corrupt,
PNG_Does_Not_Adhere_to_Spec,
PLTE_Encountered_Unexpectedly, PLTE_Encountered_Unexpectedly,
PLTE_Invalid_Length, PLTE_Invalid_Length,
TRNS_Encountered_Unexpectedly, TRNS_Encountered_Unexpectedly,
BKGD_Invalid_Length, BKGD_Invalid_Length,
Invalid_Image_Dimensions,
Unknown_Color_Type, Unknown_Color_Type,
Invalid_Color_Bit_Depth_Combo, Invalid_Color_Bit_Depth_Combo,
Unknown_Filter_Method, Unknown_Filter_Method,
+1 -2
View File
@@ -12,7 +12,6 @@ package png
An example of how to use `load`. An example of how to use `load`.
*/ */
import "core:compress"
import "core:image" import "core:image"
// import "core:image/png" // import "core:image/png"
import "core:bytes" import "core:bytes"
@@ -42,7 +41,7 @@ demo :: proc() {
file: string file: string
options := image.Options{.return_metadata} options := image.Options{.return_metadata}
err: compress.Error err: image.Error
img: ^image.Image img: ^image.Image
file = "../../../misc/logo-slim.png" file = "../../../misc/logo-slim.png"
+37 -41
View File
@@ -21,11 +21,7 @@ import "core:io"
import "core:mem" import "core:mem"
import "core:intrinsics" import "core:intrinsics"
Error :: compress.Error Error :: image.Error
E_General :: compress.General_Error
E_PNG :: image.Error
E_Deflate :: compress.Deflate_Error
Image :: image.Image Image :: image.Image
Options :: image.Options Options :: image.Options
@@ -248,13 +244,13 @@ ADAM7_Y_SPACING := []int{ 8,8,8,4,4,2,2 }
read_chunk :: proc(ctx: ^$C) -> (chunk: Chunk, err: Error) { read_chunk :: proc(ctx: ^$C) -> (chunk: Chunk, err: Error) {
ch, e := compress.read_data(ctx, Chunk_Header) ch, e := compress.read_data(ctx, Chunk_Header)
if e != .None { if e != .None {
return {}, E_General.Stream_Too_Short return {}, compress.General_Error.Stream_Too_Short
} }
chunk.header = ch chunk.header = ch
chunk.data, e = compress.read_slice(ctx, int(ch.length)) chunk.data, e = compress.read_slice(ctx, int(ch.length))
if e != .None { if e != .None {
return {}, E_General.Stream_Too_Short return {}, compress.General_Error.Stream_Too_Short
} }
// Compute CRC over chunk type + data // Compute CRC over chunk type + data
@@ -264,12 +260,12 @@ read_chunk :: proc(ctx: ^$C) -> (chunk: Chunk, err: Error) {
crc, e3 := compress.read_data(ctx, u32be) crc, e3 := compress.read_data(ctx, u32be)
if e3 != .None { if e3 != .None {
return {}, E_General.Stream_Too_Short return {}, compress.General_Error.Stream_Too_Short
} }
chunk.crc = crc chunk.crc = crc
if chunk.crc != u32be(computed_crc) { if chunk.crc != u32be(computed_crc) {
return {}, E_General.Checksum_Failed return {}, compress.General_Error.Checksum_Failed
} }
return chunk, nil return chunk, nil
} }
@@ -297,7 +293,7 @@ append_chunk :: proc(list: ^[dynamic]Chunk, src: Chunk, allocator := context.all
append(list, c) append(list, c)
if len(list) != length + 1 { if len(list) != length + 1 {
// Resize during append failed. // Resize during append failed.
return .Out_Of_Memory return mem.Allocator_Error.Out_Of_Memory
} }
return return
@@ -313,19 +309,19 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
// Validate IHDR // Validate IHDR
using header using header
if width == 0 || height == 0 { if width == 0 || height == 0 {
return {}, E_PNG.Invalid_Image_Dimensions return {}, .Invalid_Image_Dimensions
} }
if compression_method != 0 { if compression_method != 0 {
return {}, E_General.Unknown_Compression_Method return {}, compress.General_Error.Unknown_Compression_Method
} }
if filter_method != 0 { if filter_method != 0 {
return {}, E_PNG.Unknown_Filter_Method return {}, .Unknown_Filter_Method
} }
if interlace_method != .None && interlace_method != .Adam7 { if interlace_method != .None && interlace_method != .Adam7 {
return {}, E_PNG.Unknown_Interlace_Method return {}, .Unknown_Interlace_Method
} }
@@ -343,7 +339,7 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
} }
} }
if !allowed { if !allowed {
return {}, E_PNG.Invalid_Color_Bit_Depth_Combo return {}, .Invalid_Color_Bit_Depth_Combo
} }
case 2, 4, 6: case 2, 4, 6:
/* /*
@@ -351,7 +347,7 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
Allowed bit depths: 8 and 16 Allowed bit depths: 8 and 16
*/ */
if bit_depth != 8 && bit_depth != 16 { if bit_depth != 8 && bit_depth != 16 {
return {}, E_PNG.Invalid_Color_Bit_Depth_Combo return {}, .Invalid_Color_Bit_Depth_Combo
} }
case 3: case 3:
/* /*
@@ -366,11 +362,11 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
} }
} }
if !allowed { if !allowed {
return {}, E_PNG.Invalid_Color_Bit_Depth_Combo return {}, .Invalid_Color_Bit_Depth_Combo
} }
case: case:
return {}, E_PNG.Unknown_Color_Type return {}, .Unknown_Color_Type
} }
return header, nil return header, nil
@@ -406,7 +402,7 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont
return load_from_slice(data, options) return load_from_slice(data, options)
} else { } else {
img = new(Image) img = new(Image)
return img, E_General.File_Not_Found return img, compress.General_Error.File_Not_Found
} }
} }
@@ -420,7 +416,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
} }
if .alpha_drop_if_present in options && .alpha_add_if_missing in options { if .alpha_drop_if_present in options && .alpha_add_if_missing in options {
return {}, E_General.Incompatible_Options return {}, compress.General_Error.Incompatible_Options
} }
if .do_not_expand_channels in options { if .do_not_expand_channels in options {
@@ -437,7 +433,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
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 {
return img, E_PNG.Invalid_PNG_Signature return img, .Invalid_PNG_Signature
} }
idat: []u8 idat: []u8
@@ -472,14 +468,14 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
ch, e = compress.peek_data(ctx, Chunk_Header) ch, e = compress.peek_data(ctx, Chunk_Header)
if e != .None { if e != .None {
return img, E_General.Stream_Too_Short return img, compress.General_Error.Stream_Too_Short
} }
// name := chunk_type_to_name(&ch.type); // Only used for debug prints during development. // name := chunk_type_to_name(&ch.type); // Only used for debug prints during development.
#partial switch ch.type { #partial switch ch.type {
case .IHDR: case .IHDR:
if seen_ihdr || !first { if seen_ihdr || !first {
return {}, E_PNG.IHDR_Not_First_Chunk return {}, .IHDR_Not_First_Chunk
} }
seen_ihdr = true seen_ihdr = true
@@ -508,7 +504,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
} }
if img.channels == 0 || img.depth == 0 { if img.channels == 0 || img.depth == 0 {
return {}, E_PNG.IHDR_Corrupt return {}, .IHDR_Corrupt
} }
img.width = int(header.width) img.width = int(header.width)
@@ -530,18 +526,18 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
// PLTE must appear before IDAT and can't appear for color types 0, 4. // PLTE must appear before IDAT and can't appear for color types 0, 4.
ct := transmute(u8)info.header.color_type ct := transmute(u8)info.header.color_type
if seen_idat || ct == 0 || ct == 4 { if seen_idat || ct == 0 || ct == 4 {
return img, E_PNG.PLTE_Encountered_Unexpectedly return img, .PLTE_Encountered_Unexpectedly
} }
c = read_chunk(ctx) or_return c = read_chunk(ctx) or_return
if c.header.length % 3 != 0 || c.header.length > 768 { if c.header.length % 3 != 0 || c.header.length > 768 {
return img, E_PNG.PLTE_Invalid_Length return img, .PLTE_Invalid_Length
} }
plte_ok: bool plte_ok: bool
_plte, plte_ok = plte(c) _plte, plte_ok = plte(c)
if !plte_ok { if !plte_ok {
return img, E_PNG.PLTE_Invalid_Length return img, .PLTE_Invalid_Length
} }
if .return_metadata in options { if .return_metadata in options {
@@ -555,11 +551,11 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
} }
// There must be at least 1 IDAT, contiguous if more. // There must be at least 1 IDAT, contiguous if more.
if seen_idat { if seen_idat {
return img, E_PNG.IDAT_Must_Be_Contiguous return img, .IDAT_Must_Be_Contiguous
} }
if idat_length > 0 { if idat_length > 0 {
return img, E_PNG.IDAT_Must_Be_Contiguous return img, .IDAT_Must_Be_Contiguous
} }
next := ch.type next := ch.type
@@ -571,13 +567,13 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
ch, e = compress.peek_data(ctx, Chunk_Header) ch, e = compress.peek_data(ctx, Chunk_Header)
if e != .None { if e != .None {
return img, E_General.Stream_Too_Short return img, compress.General_Error.Stream_Too_Short
} }
next = ch.type next = ch.type
} }
idat = bytes.buffer_to_bytes(&idat_b) idat = bytes.buffer_to_bytes(&idat_b)
if int(idat_length) != len(idat) { if int(idat_length) != len(idat) {
return {}, E_PNG.IDAT_Corrupt return {}, .IDAT_Corrupt
} }
seen_idat = true seen_idat = true
case .IEND: case .IEND:
@@ -597,7 +593,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
switch ct { switch ct {
case 3: // Indexed color case 3: // Indexed color
if c.header.length != 1 { if c.header.length != 1 {
return {}, E_PNG.BKGD_Invalid_Length return {}, .BKGD_Invalid_Length
} }
col := _plte.entries[c.data[0]] col := _plte.entries[c.data[0]]
img.background = [3]u16{ img.background = [3]u16{
@@ -607,13 +603,13 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
} }
case 0, 4: // Grayscale, with and without Alpha case 0, 4: // Grayscale, with and without Alpha
if c.header.length != 2 { if c.header.length != 2 {
return {}, E_PNG.BKGD_Invalid_Length return {}, .BKGD_Invalid_Length
} }
col := u16(mem.slice_data_cast([]u16be, c.data[:])[0]) col := u16(mem.slice_data_cast([]u16be, c.data[:])[0])
img.background = [3]u16{col, col, col} img.background = [3]u16{col, col, col}
case 2, 6: // Color, with and without Alpha case 2, 6: // Color, with and without Alpha
if c.header.length != 6 { if c.header.length != 6 {
return {}, E_PNG.BKGD_Invalid_Length return {}, .BKGD_Invalid_Length
} }
col := mem.slice_data_cast([]u16be, c.data[:]) col := mem.slice_data_cast([]u16be, c.data[:])
img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])} img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])}
@@ -622,7 +618,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
c = read_chunk(ctx) or_return c = read_chunk(ctx) or_return
if .Alpha in info.header.color_type { if .Alpha in info.header.color_type {
return img, E_PNG.TRNS_Encountered_Unexpectedly return img, .TRNS_Encountered_Unexpectedly
} }
if .return_metadata in options { if .return_metadata in options {
@@ -655,7 +651,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
We're not going to add support for it. If you have the misfortunte of coming 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. across one of these files, use a utility to defry it.
*/ */
return img, E_PNG.PNG_Does_Not_Adhere_to_Spec return img, .Image_Does_Not_Adhere_to_Spec
case: case:
// Unhandled type // Unhandled type
c = read_chunk(ctx) or_return c = read_chunk(ctx) or_return
@@ -673,7 +669,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
} }
if !seen_idat { if !seen_idat {
return img, E_PNG.IDAT_Missing return img, .IDAT_Missing
} }
/* /*
@@ -710,7 +706,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
buf_len := len(buf.buf) buf_len := len(buf.buf)
if expected_size != buf_len { if expected_size != buf_len {
return {}, E_PNG.IDAT_Corrupt return {}, .IDAT_Corrupt
} }
/* /*
@@ -1549,7 +1545,7 @@ defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) {
return return
} }
defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, options: Options) -> (err: compress.Error) { defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^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)
@@ -1585,7 +1581,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 .Unknown_Filter_Method
} }
} else { } else {
/* /*
@@ -1623,7 +1619,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 .Unknown_Filter_Method
} }
t := temp.buf[:] t := temp.buf[:]
+4
View File
@@ -0,0 +1,4 @@
@echo off
pushd ..
odin run image
popd
+13 -13
View File
@@ -64,7 +64,7 @@ PNG_Test :: struct {
file: string, file: string,
tests: []struct { tests: []struct {
options: image.Options, options: image.Options,
expected_error: compress.Error, expected_error: image.Error,
dims: PNG_Dims, dims: PNG_Dims,
hash: u32, hash: u32,
}, },
@@ -1198,37 +1198,37 @@ Corrupt_PNG_Tests := []PNG_Test{
{ {
"xs1n0g01", // signature byte 1 MSBit reset to zero "xs1n0g01", // signature byte 1 MSBit reset to zero
{ {
{Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000}, {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000},
}, },
}, },
{ {
"xs2n0g01", // signature byte 2 is a 'Q' "xs2n0g01", // signature byte 2 is a 'Q'
{ {
{Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000}, {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000},
}, },
}, },
{ {
"xs4n0g01", // signature byte 4 lowercase "xs4n0g01", // signature byte 4 lowercase
{ {
{Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000}, {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000},
}, },
}, },
{ {
"xs7n0g01", // 7th byte a space instead of control-Z "xs7n0g01", // 7th byte a space instead of control-Z
{ {
{Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000}, {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000},
}, },
}, },
{ {
"xcrn0g04", // added cr bytes "xcrn0g04", // added cr bytes
{ {
{Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000}, {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000},
}, },
}, },
{ {
"xlfn0g04", // added lf bytes "xlfn0g04", // added lf bytes
{ {
{Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000}, {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000},
}, },
}, },
{ {
@@ -1240,37 +1240,37 @@ Corrupt_PNG_Tests := []PNG_Test{
{ {
"xc1n0g08", // color type 1 "xc1n0g08", // color type 1
{ {
{Default, I_Error.Unknown_Color_Type, {}, 0x_0000_0000}, {Default, .Unknown_Color_Type, {}, 0x_0000_0000},
}, },
}, },
{ {
"xc9n2c08", // color type 9 "xc9n2c08", // color type 9
{ {
{Default, I_Error.Unknown_Color_Type, {}, 0x_0000_0000}, {Default, .Unknown_Color_Type, {}, 0x_0000_0000},
}, },
}, },
{ {
"xd0n2c08", // bit-depth 0 "xd0n2c08", // bit-depth 0
{ {
{Default, I_Error.Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000}, {Default, .Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000},
}, },
}, },
{ {
"xd3n2c08", // bit-depth 3 "xd3n2c08", // bit-depth 3
{ {
{Default, I_Error.Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000}, {Default, .Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000},
}, },
}, },
{ {
"xd9n2c08", // bit-depth 99 "xd9n2c08", // bit-depth 99
{ {
{Default, I_Error.Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000}, {Default, .Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000},
}, },
}, },
{ {
"xdtn0g01", // missing IDAT chunk "xdtn0g01", // missing IDAT chunk
{ {
{Default, I_Error.IDAT_Missing, {}, 0x_0000_0000}, {Default, .IDAT_Missing, {}, 0x_0000_0000},
}, },
}, },
{ {