mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-13 09:22:22 -07:00
[pbm] Normalize some errors, correct .depth
This commit is contained in:
+25
-16
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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{
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -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}
|
||||
@@ -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},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user