diff --git a/core/image/common.odin b/core/image/common.odin index 2196565bd..6722036ff 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -45,7 +45,7 @@ Image :: struct { width: int, height: int, channels: int, - depth: int, + depth: int, // Channel depth in bits, typically 8 or 16 pixels: bytes.Buffer, /* Some image loaders/writers can return/take an optional background color. @@ -141,13 +141,14 @@ Option :: enum { alpha_drop_if_present, // Unimplemented for QOI. Returns error. alpha_premultiply, // Unimplemented for QOI. Returns error. blend_background, // Ignored for non-PNG formats + // Unimplemented do_not_expand_grayscale, do_not_expand_indexed, do_not_expand_channels, // SAVE OPTIONS - qoi_all_channels_linear, // QOI, informative info. If not set, defaults to sRGB with linear alpha. + qoi_all_channels_linear, // QOI, informative only. If not set, defaults to sRGB with linear alpha. } Options :: distinct bit_set[Option] @@ -166,12 +167,29 @@ Error :: union #shared_nil { General_Image_Error :: enum { None = 0, + // File I/O + Unable_To_Read_File, + Unable_To_Write_File, + + // Invalid + Invalid_Signature, + Invalid_Input_Image, + Image_Dimensions_Too_Large, Invalid_Image_Dimensions, Invalid_Number_Of_Channels, - Image_Dimensions_Too_Large, Image_Does_Not_Adhere_to_Spec, - Invalid_Input_Image, + Invalid_Image_Depth, + Invalid_Bit_Depth, + Invalid_Color_Space, + + // More data than pixels to decode into, for example. + Corrupt, + + // Output buffer is the wrong size Invalid_Output, + + // Allocation + Unable_To_Allocate_Or_Resize, } /* @@ -201,8 +219,6 @@ Netpbm_Error :: enum { None = 0, // reading - File_Not_Readable, - Invalid_Signature, Invalid_Header_Token_Character, Incomplete_Header, Invalid_Header_Value, @@ -212,9 +228,7 @@ Netpbm_Error :: enum { Invalid_Buffer_Value, // writing - File_Not_Writable, Invalid_Format, - Invalid_Image_Depth, } /* @@ -222,7 +236,6 @@ Netpbm_Error :: enum { */ PNG_Error :: enum { None = 0, - Invalid_PNG_Signature, IHDR_Not_First_Chunk, IHDR_Corrupt, IDAT_Missing, @@ -338,14 +351,10 @@ PNG_Interlace_Method :: enum u8 { */ QOI_Error :: enum { None = 0, - Invalid_QOI_Signature, - Invalid_Bit_Depth, // QOI supports only 8-bit images, error only returned from writer. - Invalid_Color_Space, // QOI allows 0 = sRGB or 1 = linear. - Corrupt, // More data than pixels to decode into, for example. Missing_Or_Corrupt_Trailer, // Image seemed to have decoded okay, but trailer is missing or corrupt. } -QOI_Magic :: u32be(0x716f6966) // "qoif" +QOI_Magic :: u32be(0x716f6966) // "qoif" QOI_Color_Space :: enum u8 { sRGB = 0, @@ -1170,10 +1179,10 @@ write_bytes :: proc(buf: ^bytes.Buffer, data: []u8) -> (err: compress.General_Er return nil } else if len(data) == 1 { if bytes.buffer_write_byte(buf, data[0]) != nil { - return compress.General_Error.Resize_Failed + return .Resize_Failed } } else if n, _ := bytes.buffer_write(buf, data); n != len(data) { - return compress.General_Error.Resize_Failed + return .Resize_Failed } return nil } \ No newline at end of file diff --git a/core/image/netpbm/netpbm.odin b/core/image/netpbm/netpbm.odin index 5c082e384..54935d6c6 100644 --- a/core/image/netpbm/netpbm.odin +++ b/core/image/netpbm/netpbm.odin @@ -9,8 +9,6 @@ import "core:strconv" import "core:strings" import "core:unicode" - - Image :: image.Image Format :: image.Netpbm_Format Header :: image.Netpbm_Header @@ -18,8 +16,6 @@ Info :: image.Netpbm_Info Error :: image.Error Format_Error :: image.Netpbm_Error - - Formats :: bit_set[Format] PBM :: Formats{.P1, .P4} PGM :: Formats{.P2, .P5} @@ -30,14 +26,12 @@ PFM :: Formats{.Pf, .PF} ASCII :: Formats{.P1, .P2, .P3} BINARY :: Formats{.P4, .P5, .P6} + PAM + PFM - - -read :: proc { - read_from_file, - read_from_buffer, +load :: proc { + load_from_file, + load_from_buffer, } -read_from_file :: proc(filename: string, allocator := context.allocator) -> (img: Image, err: Error) { +load_from_file :: proc(filename: string, allocator := context.allocator) -> (img: Image, err: Error) { context.allocator = allocator data, ok := os.read_entire_file(filename); defer delete(data) @@ -49,7 +43,7 @@ read_from_file :: proc(filename: string, allocator := context.allocator) -> (img return read_from_buffer(data) } -read_from_buffer :: proc(data: []byte, allocator := context.allocator) -> (img: Image, err: Error) { +load_from_buffer :: proc(data: []byte, allocator := context.allocator) -> (img: Image, err: Error) { context.allocator = allocator header: Header; defer header_destroy(&header) @@ -70,14 +64,12 @@ read_from_buffer :: proc(data: []byte, allocator := context.allocator) -> (img: return } - - -write :: proc { - write_to_file, - write_to_buffer, +save :: proc { + save_to_file, + save_to_buffer, } -write_to_file :: proc(filename: string, img: Image, allocator := context.allocator) -> (err: Error) { +save_to_file :: proc(filename: string, img: Image, allocator := context.allocator) -> (err: Error) { context.allocator = allocator data: []byte; defer delete(data) @@ -90,7 +82,7 @@ write_to_file :: proc(filename: string, img: Image, allocator := context.allocat return Format_Error.None } -write_to_buffer :: proc(img: Image, allocator := context.allocator) -> (buffer: []byte, err: Error) { +save_to_buffer :: proc(img: Image, allocator := context.allocator) -> (buffer: []byte, err: Error) { context.allocator = allocator info, ok := img.metadata.(^image.Netpbm_Info) @@ -109,12 +101,12 @@ write_to_buffer :: proc(img: Image, allocator := context.allocator) -> (buffer: } if header.format in (PNM + PAM) { - if header.maxval <= int(max(u8)) && img.depth != 1 \ - || header.maxval > int(max(u8)) && header.maxval <= int(max(u16)) && img.depth != 2 { + if header.maxval <= int(max(u8)) && img.depth != 8 \ + || header.maxval > int(max(u8)) && header.maxval <= int(max(u16)) && img.depth != 16 { err = Format_Error.Invalid_Image_Depth return } - } else if header.format in PFM && img.depth != 4 { + } else if header.format in PFM && img.depth != 32 { err = Format_Error.Invalid_Image_Depth return } @@ -179,7 +171,7 @@ write_to_buffer :: proc(img: Image, allocator := context.allocator) -> (buffer: mem.copy(raw_data(data.buf[len(header_buf):]), raw_data(pixels), len(pixels)) // convert from native endianness - if img.depth == 2 { + if img.depth == 16 { pixels := mem.slice_data_cast([]u16be, data.buf[len(header_buf):]) for p in &pixels { p = u16be(transmute(u16) p) @@ -212,7 +204,7 @@ write_to_buffer :: proc(img: Image, allocator := context.allocator) -> (buffer: // Token ASCII case .P2, .P3: switch img.depth { - case 1: + case 8: pixels := img.pixels.buf[:] for y in 0 ..< img.height { for x in 0 ..< img.width { @@ -226,7 +218,7 @@ write_to_buffer :: proc(img: Image, allocator := context.allocator) -> (buffer: fmt.sbprint(&data, "\n") } - case 2: + case 16: pixels := mem.slice_data_cast([]u16, img.pixels.buf[:]) for y in 0 ..< img.height { for x in 0 ..< img.width { @@ -251,8 +243,6 @@ write_to_buffer :: proc(img: Image, allocator := context.allocator) -> (buffer: return data.buf[:], Format_Error.None } - - parse_header :: proc(data: []byte, allocator := context.allocator) -> (header: Header, length: int, err: Error) { context.allocator = allocator @@ -351,7 +341,7 @@ _parse_header_pnm :: proc(data: []byte) -> (header: Header, length: int, err: Er // set extra info header.channels = 3 if header.format in PPM else 1 - header.depth = 2 if header.maxval > int(max(u8)) else 1 + header.depth = 16 if header.maxval > int(max(u8)) else 8 // limit checking if current_field < len(header_fields) { @@ -448,12 +438,11 @@ _parse_header_pam :: proc(data: []byte, allocator := context.allocator) -> (head } // extra info - header.depth = 2 if header.maxval > int(max(u8)) else 1 + header.depth = 16 if header.maxval > int(max(u8)) else 8 // limit checking if header.width < 1 \ || header.height < 1 \ - || header.depth < 1 \ || header.maxval < 1 \ || header.maxval > int(max(u16)) { err = Format_Error.Invalid_Header_Value @@ -484,7 +473,7 @@ _parse_header_pfm :: proc(data: []byte) -> (header: Header, length: int, err: Er } // floating point - header.depth = 4 + header.depth = 32 // width field, ok = strings.fields_iterator(&field_iterator) @@ -542,8 +531,6 @@ _parse_header_pfm :: proc(data: []byte) -> (header: Header, length: int, err: Er return } - - decode_image :: proc(header: Header, data: []byte, allocator := context.allocator) -> (img: Image, err: Error) { context.allocator = allocator @@ -554,7 +541,7 @@ decode_image :: proc(header: Header, data: []byte, allocator := context.allocato depth = header.depth, } - buffer_size := img.width * img.height * img.channels * img.depth + buffer_size := image.compute_buffer_size(img.width, img.height, img.channels, img.depth) // we can check data size for binary formats if header.format in BINARY { @@ -615,7 +602,7 @@ decode_image :: proc(header: Header, data: []byte, allocator := context.allocato } } } else { - if img.depth == 2 { + if img.depth == 16 { pixels := mem.slice_data_cast([]u16, img.pixels.buf[:]) for p in &pixels { p = u16(transmute(u16be) p) @@ -658,9 +645,9 @@ decode_image :: proc(header: Header, data: []byte, allocator := context.allocato } switch img.depth { - case 1: + case 8: bytes.buffer_write_byte(&img.pixels, u8(value)) - case 2: + case 16: vb := transmute([2]u8) u16(value) bytes.buffer_write(&img.pixels, vb[:]) } @@ -678,4 +665,4 @@ decode_image :: proc(header: Header, data: []byte, allocator := context.allocato err = Format_Error.None return -} +} \ No newline at end of file diff --git a/core/image/png/png.odin b/core/image/png/png.odin index ba888cb78..d526dfb27 100644 --- a/core/image/png/png.odin +++ b/core/image/png/png.odin @@ -238,7 +238,7 @@ append_chunk :: proc(list: ^[dynamic]image.PNG_Chunk, src: image.PNG_Chunk, allo append(list, c) if len(list) != length + 1 { // Resize during append failed. - return mem.Allocator_Error.Out_Of_Memory + return .Unable_To_Allocate_Or_Resize } return @@ -347,7 +347,7 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont return load_from_slice(data, options) } else { img = new(Image) - return img, compress.General_Error.File_Not_Found + return img, .Unable_To_Read_File } } @@ -381,7 +381,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, .Invalid_PNG_Signature + return img, .Invalid_Signature } idat: []u8 @@ -747,7 +747,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8) t := bytes.Buffer{} if !resize(&t.buf, dest_raw_size) { - return {}, mem.Allocator_Error.Out_Of_Memory + return {}, .Unable_To_Allocate_Or_Resize } i := 0; j := 0 @@ -828,7 +828,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 16) t := bytes.Buffer{} if !resize(&t.buf, dest_raw_size) { - return {}, mem.Allocator_Error.Out_Of_Memory + return {}, .Unable_To_Allocate_Or_Resize } p16 := mem.slice_data_cast([]u16, temp.buf[:]) @@ -1027,7 +1027,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8) t := bytes.Buffer{} if !resize(&t.buf, dest_raw_size) { - return {}, mem.Allocator_Error.Out_Of_Memory + return {}, .Unable_To_Allocate_Or_Resize } p := mem.slice_data_cast([]u8, temp.buf[:]) @@ -1535,7 +1535,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH num_bytes := compute_buffer_size(width, height, channels, depth == 16 ? 16 : 8) if !resize(&img.pixels.buf, num_bytes) { - return mem.Allocator_Error.Out_Of_Memory + return .Unable_To_Allocate_Or_Resize } filter_ok: bool @@ -1577,7 +1577,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH temp: bytes.Buffer temp_len := compute_buffer_size(x, y, channels, depth == 16 ? 16 : 8) if !resize(&temp.buf, temp_len) { - return mem.Allocator_Error.Out_Of_Memory + return .Unable_To_Allocate_Or_Resize } params := Filter_Params{ diff --git a/core/image/qoi/qoi.odin b/core/image/qoi/qoi.odin index fdbaab686..83b212be8 100644 --- a/core/image/qoi/qoi.odin +++ b/core/image/qoi/qoi.odin @@ -12,14 +12,12 @@ // The QOI specification is at https://qoiformat.org. package qoi -import "core:mem" import "core:image" import "core:compress" import "core:bytes" import "core:os" Error :: image.Error -General :: compress.General_Error Image :: image.Image Options :: image.Options @@ -57,7 +55,7 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{} max_size := pixels * (img.channels + 1) + size_of(image.QOI_Header) + size_of(u64be) if !resize(&output.buf, max_size) { - return General.Resize_Failed + return .Unable_To_Allocate_Or_Resize } header := image.QOI_Header{ @@ -177,7 +175,7 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato save_to_memory(out, img, options) or_return write_ok := os.write_entire_file(output, out.buf[:]) - return nil if write_ok else General.Cannot_Open_File + return nil if write_ok else .Unable_To_Write_File } save :: proc{save_to_memory, save_to_file} @@ -201,7 +199,7 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont return load_from_slice(data, options) } else { img = new(Image) - return img, compress.General_Error.File_Not_Found + return img, .Unable_To_Read_File } } @@ -221,7 +219,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a header := image.read_data(ctx, image.QOI_Header) or_return if header.magic != image.QOI_Magic { - return img, .Invalid_QOI_Signature + return img, .Invalid_Signature } if img == nil { @@ -264,7 +262,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a bytes_needed := image.compute_buffer_size(int(header.width), int(header.height), img.channels, 8) if !resize(&img.pixels.buf, bytes_needed) { - return img, mem.Allocator_Error.Out_Of_Memory + return img, .Unable_To_Allocate_Or_Resize } /* diff --git a/core/image/tga/tga.odin b/core/image/tga/tga.odin index 3c860cb62..0539706b3 100644 --- a/core/image/tga/tga.odin +++ b/core/image/tga/tga.odin @@ -17,7 +17,6 @@ import "core:bytes" import "core:os" Error :: image.Error -General :: compress.General_Error Image :: image.Image Options :: image.Options @@ -55,7 +54,7 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{} necessary := pixels * img.channels + size_of(image.TGA_Header) if !resize(&output.buf, necessary) { - return General.Resize_Failed + return .Unable_To_Allocate_Or_Resize } header := image.TGA_Header{ @@ -97,7 +96,7 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato save_to_memory(out, img, options) or_return write_ok := os.write_entire_file(output, out.buf[:]) - return nil if write_ok else General.Cannot_Open_File + return nil if write_ok else .Unable_To_Write_File } save :: proc{save_to_memory, save_to_file} \ 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 0c11ca5ae..c328757e4 100644 --- a/tests/core/image/test_core_image.odin +++ b/tests/core/image/test_core_image.odin @@ -1199,37 +1199,37 @@ Corrupt_PNG_Tests := []PNG_Test{ { "xs1n0g01", // signature byte 1 MSBit reset to zero { - {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_Signature, {}, 0x_0000_0000}, }, }, { "xs2n0g01", // signature byte 2 is a 'Q' { - {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_Signature, {}, 0x_0000_0000}, }, }, { "xs4n0g01", // signature byte 4 lowercase { - {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_Signature, {}, 0x_0000_0000}, }, }, { "xs7n0g01", // 7th byte a space instead of control-Z { - {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_Signature, {}, 0x_0000_0000}, }, }, { "xcrn0g04", // added cr bytes { - {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_Signature, {}, 0x_0000_0000}, }, }, { "xlfn0g04", // added lf bytes { - {Default, .Invalid_PNG_Signature, {}, 0x_0000_0000}, + {Default, .Invalid_Signature, {}, 0x_0000_0000}, }, }, {