diff options
| -rw-r--r-- | core/image/qoi/qoi.odin | 29 | ||||
| -rw-r--r-- | tests/core/image/test_core_image.odin | 34 |
2 files changed, 44 insertions, 19 deletions
diff --git a/core/image/qoi/qoi.odin b/core/image/qoi/qoi.odin index c157e8099..fdbaab686 100644 --- a/core/image/qoi/qoi.odin +++ b/core/image/qoi/qoi.odin @@ -210,11 +210,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a context.allocator = allocator
options := options
- if .alpha_drop_if_present in options || .alpha_premultiply in options {
- // TODO: Implement.
- // As stated in image/common, unimplemented options are ignored.
- }
-
if .info in options {
options |= {.return_metadata, .do_not_decompress_image}
options -= {.info}
@@ -258,19 +253,19 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a img.width = int(header.width)
img.height = int(header.height)
- img.channels = 4
+ img.channels = 4 if .alpha_add_if_missing in options else int(header.channels)
img.depth = 8
if .do_not_decompress_image in options {
+ img.channels = int(header.channels)
return
}
- bytes_needed := image.compute_buffer_size(int(header.width), int(header.height), 4, 8)
+ 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
}
- pixels := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:])
/*
Decode loop starts here.
@@ -278,6 +273,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a seen: [64]RGBA_Pixel
pix := RGBA_Pixel{0, 0, 0, 255}
seen[qoi_hash(pix)] = pix
+ pixels := img.pixels.buf[:]
decode: for len(pixels) > 0 {
data := image.read_u8(ctx) or_return
@@ -330,13 +326,13 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a }
case .RUN:
- if length := int(data & 63) + 1; length > len(pixels) {
+ if length := int(data & 63) + 1; (length * img.channels) > len(pixels) {
return img, .Corrupt
} else {
- #no_bounds_check for i in 0..<length {
- pixels[i] = pix
+ #no_bounds_check for in 0..<length {
+ copy(pixels, pix[:img.channels])
+ pixels = pixels[img.channels:]
}
- pixels = pixels[length:]
}
continue decode
@@ -347,8 +343,8 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a }
#no_bounds_check {
- pixels[0] = pix
- pixels = pixels[1:]
+ copy(pixels, pix[:img.channels])
+ pixels = pixels[img.channels:]
}
}
@@ -357,6 +353,11 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a if trailer_err != nil || trailer != 0x1 {
return img, .Missing_Or_Corrupt_Trailer
}
+
+ if .alpha_premultiply in options && !image.alpha_drop_if_present(img, options) {
+ return img, .Post_Processing_Error
+ }
+
return
}
diff --git a/tests/core/image/test_core_image.odin b/tests/core/image/test_core_image.odin index 38291db6b..0c11ca5ae 100644 --- a/tests/core/image/test_core_image.odin +++ b/tests/core/image/test_core_image.odin @@ -5,7 +5,7 @@ List of contributors: Jeroen van Rijn: Initial implementation. - A test suite for PNG. + A test suite for PNG + QOI. */ package test_core_image @@ -14,6 +14,7 @@ import "core:testing" import "core:compress" import "core:image" import "core:image/png" +import "core:image/qoi" import "core:bytes" import "core:hash" @@ -1499,11 +1500,34 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) { passed &= dims_pass - hash := hash.crc32(pixels) - error = fmt.tprintf("%v test %v hash is %08x, expected %08x with %v.", file.file, count, hash, test.hash, test.options) - expect(t, test.hash == hash, error) + png_hash := hash.crc32(pixels) + error = fmt.tprintf("%v test %v hash is %08x, expected %08x with %v.", file.file, count, png_hash, test.hash, test.options) + expect(t, test.hash == png_hash, error) + + passed &= test.hash == png_hash + + // Roundtrip through QOI to test the QOI encoder and decoder. + if passed && img.depth == 8 && (img.channels == 3 || img.channels == 4) { + qoi_buffer: bytes.Buffer + defer bytes.buffer_destroy(&qoi_buffer) + qoi_save_err := qoi.save(&qoi_buffer, img) + + error = fmt.tprintf("%v test %v QOI save failed with %v.", file.file, count, qoi_save_err) + expect(t, qoi_save_err == nil, error) + + if qoi_save_err == nil { + qoi_img, qoi_load_err := qoi.load(qoi_buffer.buf[:]) + defer qoi.destroy(qoi_img) + + error = fmt.tprintf("%v test %v QOI load failed with %v.", file.file, count, qoi_load_err) + expect(t, qoi_load_err == nil, error) + + qoi_hash := hash.crc32(qoi_img.pixels.buf[:]) + error = fmt.tprintf("%v test %v QOI load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, qoi_hash, png_hash, test.options) + expect(t, qoi_hash == png_hash, error) + } + } - passed &= test.hash == hash if .return_metadata in test.options { if v, ok := img.metadata.(^image.PNG_Info); ok { |