mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-04 14:48:47 +00:00
[QOI] Add support for RGB images (previously loader always output RGBA).
Also add QOI to CI test suite by roundtripping 8-bit RGB(A) through QOI and checking the hashes match.
This commit is contained in:
+15
-14
@@ -210,11 +210,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
options := options
|
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 {
|
if .info in options {
|
||||||
options |= {.return_metadata, .do_not_decompress_image}
|
options |= {.return_metadata, .do_not_decompress_image}
|
||||||
options -= {.info}
|
options -= {.info}
|
||||||
@@ -258,19 +253,19 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
|
|
||||||
img.width = int(header.width)
|
img.width = int(header.width)
|
||||||
img.height = int(header.height)
|
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
|
img.depth = 8
|
||||||
|
|
||||||
if .do_not_decompress_image in options {
|
if .do_not_decompress_image in options {
|
||||||
|
img.channels = int(header.channels)
|
||||||
return
|
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) {
|
if !resize(&img.pixels.buf, bytes_needed) {
|
||||||
return img, mem.Allocator_Error.Out_Of_Memory
|
return img, mem.Allocator_Error.Out_Of_Memory
|
||||||
}
|
}
|
||||||
pixels := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:])
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Decode loop starts here.
|
Decode loop starts here.
|
||||||
@@ -278,6 +273,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
seen: [64]RGBA_Pixel
|
seen: [64]RGBA_Pixel
|
||||||
pix := RGBA_Pixel{0, 0, 0, 255}
|
pix := RGBA_Pixel{0, 0, 0, 255}
|
||||||
seen[qoi_hash(pix)] = pix
|
seen[qoi_hash(pix)] = pix
|
||||||
|
pixels := img.pixels.buf[:]
|
||||||
|
|
||||||
decode: for len(pixels) > 0 {
|
decode: for len(pixels) > 0 {
|
||||||
data := image.read_u8(ctx) or_return
|
data := image.read_u8(ctx) or_return
|
||||||
@@ -330,13 +326,13 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
}
|
}
|
||||||
|
|
||||||
case .RUN:
|
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
|
return img, .Corrupt
|
||||||
} else {
|
} else {
|
||||||
#no_bounds_check for i in 0..<length {
|
#no_bounds_check for in 0..<length {
|
||||||
pixels[i] = pix
|
copy(pixels, pix[:img.channels])
|
||||||
|
pixels = pixels[img.channels:]
|
||||||
}
|
}
|
||||||
pixels = pixels[length:]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
continue decode
|
continue decode
|
||||||
@@ -347,8 +343,8 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
|||||||
}
|
}
|
||||||
|
|
||||||
#no_bounds_check {
|
#no_bounds_check {
|
||||||
pixels[0] = pix
|
copy(pixels, pix[:img.channels])
|
||||||
pixels = pixels[1:]
|
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 {
|
if trailer_err != nil || trailer != 0x1 {
|
||||||
return img, .Missing_Or_Corrupt_Trailer
|
return img, .Missing_Or_Corrupt_Trailer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if .alpha_premultiply in options && !image.alpha_drop_if_present(img, options) {
|
||||||
|
return img, .Post_Processing_Error
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
List of contributors:
|
List of contributors:
|
||||||
Jeroen van Rijn: Initial implementation.
|
Jeroen van Rijn: Initial implementation.
|
||||||
|
|
||||||
A test suite for PNG.
|
A test suite for PNG + QOI.
|
||||||
*/
|
*/
|
||||||
package test_core_image
|
package test_core_image
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@ import "core:testing"
|
|||||||
import "core:compress"
|
import "core:compress"
|
||||||
import "core:image"
|
import "core:image"
|
||||||
import "core:image/png"
|
import "core:image/png"
|
||||||
|
import "core:image/qoi"
|
||||||
|
|
||||||
import "core:bytes"
|
import "core:bytes"
|
||||||
import "core:hash"
|
import "core:hash"
|
||||||
@@ -1499,11 +1500,34 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) -> (subtotal: int) {
|
|||||||
|
|
||||||
passed &= dims_pass
|
passed &= dims_pass
|
||||||
|
|
||||||
hash := hash.crc32(pixels)
|
png_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)
|
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 == hash, error)
|
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 .return_metadata in test.options {
|
||||||
|
|
||||||
if v, ok := img.metadata.(^image.PNG_Info); ok {
|
if v, ok := img.metadata.(^image.PNG_Info); ok {
|
||||||
|
|||||||
Reference in New Issue
Block a user