Browse Source

png + compress: Rearrange error unions.

Jeroen van Rijn 3 years ago
parent
commit
9b5ae95677

+ 3 - 9
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
 	*/

+ 20 - 3
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,

+ 1 - 2
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"

+ 37 - 41
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[:]

+ 4 - 0
tests/core/image/build.bat

@@ -0,0 +1,4 @@
+@echo off
+pushd ..
+odin run image
+popd

+ 13 - 13
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},
 		},
 	},
 	{