From 9b5ae9567790d0f340d6ef56ff993e39f0b4873a Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 6 Oct 2021 20:45:29 +0200 Subject: [PATCH] png + compress: Rearrange error unions. --- core/compress/common.odin | 12 ++--- core/image/common.odin | 23 ++++++-- core/image/png/example.odin | 3 +- core/image/png/png.odin | 78 +++++++++++++-------------- tests/core/image/build.bat | 4 ++ tests/core/image/test_core_image.odin | 26 ++++----- 6 files changed, 78 insertions(+), 68 deletions(-) create mode 100644 tests/core/image/build.bat diff --git a/core/compress/common.odin b/core/compress/common.odin index 5951f8bd0..819cfb481 100644 --- a/core/compress/common.odin +++ b/core/compress/common.odin @@ -9,9 +9,8 @@ package compress */ import "core:io" -import "core:image" import "core:bytes" -import "core:mem" +import "core:runtime" /* These settings bound how much compression algorithms will allocate for their output buffer. @@ -48,16 +47,12 @@ when size_of(uintptr) == 8 { Error :: union { General_Error, - mem.Allocator_Error, Deflate_Error, ZLIB_Error, GZIP_Error, ZIP_Error, - /* - This is here because png.load will return a this type of error union, - as it may involve an I/O error, a Deflate error, etc. - */ - image.Error, + + runtime.Allocator_Error, } General_Error :: enum { @@ -71,7 +66,6 @@ General_Error :: enum { Incompatible_Options, Unimplemented, - /* Memory errors */ diff --git a/core/image/common.odin b/core/image/common.odin index 5de4bb84e..9bb99a5d4 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -11,6 +11,8 @@ package image import "core:bytes" import "core:mem" +import "core:compress" +import "core:runtime" Image :: struct { width: int, @@ -112,19 +114,34 @@ Option :: enum { } 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, IHDR_Not_First_Chunk, IHDR_Corrupt, IDAT_Missing, IDAT_Must_Be_Contiguous, IDAT_Corrupt, - PNG_Does_Not_Adhere_to_Spec, PLTE_Encountered_Unexpectedly, PLTE_Invalid_Length, TRNS_Encountered_Unexpectedly, BKGD_Invalid_Length, - Invalid_Image_Dimensions, Unknown_Color_Type, Invalid_Color_Bit_Depth_Combo, Unknown_Filter_Method, diff --git a/core/image/png/example.odin b/core/image/png/example.odin index b047f5c6a..ad0423d67 100644 --- a/core/image/png/example.odin +++ b/core/image/png/example.odin @@ -12,7 +12,6 @@ package png An example of how to use `load`. */ -import "core:compress" import "core:image" // import "core:image/png" import "core:bytes" @@ -42,7 +41,7 @@ demo :: proc() { file: string options := image.Options{.return_metadata} - err: compress.Error + err: image.Error img: ^image.Image file = "../../../misc/logo-slim.png" diff --git a/core/image/png/png.odin b/core/image/png/png.odin index 204f98b66..21b27fc82 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -21,11 +21,7 @@ import "core:io" import "core:mem" import "core:intrinsics" -Error :: compress.Error -E_General :: compress.General_Error -E_PNG :: image.Error -E_Deflate :: compress.Deflate_Error - +Error :: image.Error Image :: image.Image 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) { ch, e := compress.read_data(ctx, Chunk_Header) if e != .None { - return {}, E_General.Stream_Too_Short + return {}, compress.General_Error.Stream_Too_Short } chunk.header = ch chunk.data, e = compress.read_slice(ctx, int(ch.length)) if e != .None { - return {}, E_General.Stream_Too_Short + return {}, compress.General_Error.Stream_Too_Short } // 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) if e3 != .None { - return {}, E_General.Stream_Too_Short + return {}, compress.General_Error.Stream_Too_Short } chunk.crc = crc if chunk.crc != u32be(computed_crc) { - return {}, E_General.Checksum_Failed + return {}, compress.General_Error.Checksum_Failed } return chunk, nil } @@ -297,7 +293,7 @@ append_chunk :: proc(list: ^[dynamic]Chunk, src: Chunk, allocator := context.all append(list, c) if len(list) != length + 1 { // Resize during append failed. - return .Out_Of_Memory + return mem.Allocator_Error.Out_Of_Memory } return @@ -313,19 +309,19 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) { // Validate IHDR using header if width == 0 || height == 0 { - return {}, E_PNG.Invalid_Image_Dimensions + return {}, .Invalid_Image_Dimensions } if compression_method != 0 { - return {}, E_General.Unknown_Compression_Method + return {}, compress.General_Error.Unknown_Compression_Method } if filter_method != 0 { - return {}, E_PNG.Unknown_Filter_Method + return {}, .Unknown_Filter_Method } 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 { - return {}, E_PNG.Invalid_Color_Bit_Depth_Combo + return {}, .Invalid_Color_Bit_Depth_Combo } case 2, 4, 6: /* @@ -351,7 +347,7 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) { Allowed bit depths: 8 and 16 */ if bit_depth != 8 && bit_depth != 16 { - return {}, E_PNG.Invalid_Color_Bit_Depth_Combo + return {}, .Invalid_Color_Bit_Depth_Combo } case 3: /* @@ -366,11 +362,11 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) { } } if !allowed { - return {}, E_PNG.Invalid_Color_Bit_Depth_Combo + return {}, .Invalid_Color_Bit_Depth_Combo } case: - return {}, E_PNG.Unknown_Color_Type + return {}, .Unknown_Color_Type } return header, nil @@ -406,7 +402,7 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont return load_from_slice(data, options) } else { 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 { - return {}, E_General.Incompatible_Options + return {}, compress.General_Error.Incompatible_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) if io_error != .None || signature != .PNG { - return img, E_PNG.Invalid_PNG_Signature + return img, .Invalid_PNG_Signature } 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) 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. #partial switch ch.type { case .IHDR: if seen_ihdr || !first { - return {}, E_PNG.IHDR_Not_First_Chunk + return {}, .IHDR_Not_First_Chunk } 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 { - return {}, E_PNG.IHDR_Corrupt + return {}, .IHDR_Corrupt } 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. ct := transmute(u8)info.header.color_type 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 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, plte_ok = plte(c) if !plte_ok { - return img, E_PNG.PLTE_Invalid_Length + return img, .PLTE_Invalid_Length } 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. if seen_idat { - return img, E_PNG.IDAT_Must_Be_Contiguous + return img, .IDAT_Must_Be_Contiguous } if idat_length > 0 { - return img, E_PNG.IDAT_Must_Be_Contiguous + return img, .IDAT_Must_Be_Contiguous } 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) if e != .None { - return img, E_General.Stream_Too_Short + return img, compress.General_Error.Stream_Too_Short } next = ch.type } idat = bytes.buffer_to_bytes(&idat_b) if int(idat_length) != len(idat) { - return {}, E_PNG.IDAT_Corrupt + return {}, .IDAT_Corrupt } seen_idat = true case .IEND: @@ -597,7 +593,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a switch ct { case 3: // Indexed color if c.header.length != 1 { - return {}, E_PNG.BKGD_Invalid_Length + return {}, .BKGD_Invalid_Length } col := _plte.entries[c.data[0]] 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 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]) 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 + return {}, .BKGD_Invalid_Length } col := mem.slice_data_cast([]u16be, c.data[:]) 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 if .Alpha in info.header.color_type { - return img, E_PNG.TRNS_Encountered_Unexpectedly + return img, .TRNS_Encountered_Unexpectedly } 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 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: // Unhandled type c = read_chunk(ctx) or_return @@ -673,7 +669,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a } 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) 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 } -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) width := int(header.width) height := int(header.height) @@ -1585,7 +1581,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option } if !filter_ok { // Caller will destroy buffer for us. - return E_PNG.Unknown_Filter_Method + return .Unknown_Filter_Method } } else { /* @@ -1623,7 +1619,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^IHDR, option if !filter_ok { // Caller will destroy buffer for us. - return E_PNG.Unknown_Filter_Method + return .Unknown_Filter_Method } t := temp.buf[:] diff --git a/tests/core/image/build.bat b/tests/core/image/build.bat new file mode 100644 index 000000000..03ee6b9a5 --- /dev/null +++ b/tests/core/image/build.bat @@ -0,0 +1,4 @@ +@echo off +pushd .. +odin run image +popd \ No newline at end of file diff --git a/tests/core/image/test_core_image.odin b/tests/core/image/test_core_image.odin index 14f7db074..d2db9d11d 100644 --- a/tests/core/image/test_core_image.odin +++ b/tests/core/image/test_core_image.odin @@ -64,7 +64,7 @@ PNG_Test :: struct { file: string, tests: []struct { options: image.Options, - expected_error: compress.Error, + expected_error: image.Error, dims: PNG_Dims, hash: u32, }, @@ -1198,37 +1198,37 @@ Corrupt_PNG_Tests := []PNG_Test{ { "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' { - {Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, }, }, { "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 { - {Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, }, }, { "xcrn0g04", // added cr bytes { - {Default, I_Error.Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, }, }, { "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 { - {Default, I_Error.Unknown_Color_Type, {}, 0x_0000_0000}, + {Default, .Unknown_Color_Type, {}, 0x_0000_0000}, }, }, { "xc9n2c08", // color type 9 { - {Default, I_Error.Unknown_Color_Type, {}, 0x_0000_0000}, + {Default, .Unknown_Color_Type, {}, 0x_0000_0000}, }, }, { "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 { - {Default, I_Error.Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000}, + {Default, .Invalid_Color_Bit_Depth_Combo, {}, 0x_0000_0000}, }, }, { "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 { - {Default, I_Error.IDAT_Missing, {}, 0x_0000_0000}, + {Default, .IDAT_Missing, {}, 0x_0000_0000}, }, }, {