This commit is contained in:
Jeroen van Rijn
2024-06-06 16:32:18 +02:00
parent 3a9b86628a
commit 678fdae966
5 changed files with 1382 additions and 71 deletions
+581 -71
View File
@@ -1,11 +1,11 @@
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Copyright 2021-2024 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
List of contributors:
Jeroen van Rijn: Initial implementation.
A test suite for PNG + QOI.
A test suite for PNG, TGA, NetPBM, QOI and BMP.
*/
package test_core_image
@@ -13,6 +13,7 @@ import "core:testing"
import "core:compress"
import "core:image"
import "core:image/bmp"
import pbm "core:image/netpbm"
import "core:image/png"
import "core:image/qoi"
@@ -24,16 +25,17 @@ import "core:strings"
import "core:mem"
import "core:time"
TEST_SUITE_PATH :: ODIN_ROOT + "tests/core/assets/PNG/"
TEST_SUITE_PATH_PNG :: ODIN_ROOT + "tests/core/assets/PNG"
TEST_SUITE_PATH_BMP :: ODIN_ROOT + "tests/core/assets/BMP"
I_Error :: image.Error
PNG_Test :: struct {
Test :: struct {
file: string,
tests: []struct {
options: image.Options,
expected_error: image.Error,
dims: PNG_Dims,
dims: Dims,
hash: u32,
},
}
@@ -46,19 +48,18 @@ Blend_BG_Keep :: image.Options{.blend_background, .alpha_add_if_missing}
Return_Metadata :: image.Options{.return_metadata}
No_Channel_Expansion :: image.Options{.do_not_expand_channels, .return_metadata}
PNG_Dims :: struct {
Dims :: struct {
width: int,
height: int,
channels: int,
depth: int,
}
Basic_PNG_Tests := []PNG_Test{
Basic_PNG_Tests := []Test{
/*
Basic format tests:
http://www.schaik.com/pngsuite/pngsuite_bas_png.html
*/
{
"basn0g01", // Black and white.
{
@@ -166,7 +167,7 @@ Basic_PNG_Tests := []PNG_Test{
},
}
Interlaced_PNG_Tests := []PNG_Test{
Interlaced_PNG_Tests := []Test{
/*
Interlaced format tests:
http://www.schaik.com/pngsuite/pngsuite_int_png.html
@@ -284,9 +285,9 @@ Interlaced_PNG_Tests := []PNG_Test{
},
}
Odd_Sized_PNG_Tests := []PNG_Test{
Odd_Sized_PNG_Tests := []Test{
/*
" PngSuite", // Odd sizes / PNG-files:
"PngSuite", // Odd sizes / PNG-files:
http://www.schaik.com/pngsuite/pngsuite_siz_png.html
This tests curious sizes with and without interlacing.
@@ -510,7 +511,7 @@ Odd_Sized_PNG_Tests := []PNG_Test{
},
}
PNG_bKGD_Tests := []PNG_Test{
PNG_bKGD_Tests := []Test{
/*
" PngSuite", // Background colors / PNG-files:
http://www.schaik.com/pngsuite/pngsuite_bck_png.html
@@ -597,7 +598,7 @@ PNG_bKGD_Tests := []PNG_Test{
},
}
PNG_tRNS_Tests := []PNG_Test{
PNG_tRNS_Tests := []Test{
/*
PngSuite - Transparency:
http://www.schaik.com/pngsuite/pngsuite_trn_png.html
@@ -759,7 +760,7 @@ PNG_tRNS_Tests := []PNG_Test{
},
}
PNG_Filter_Tests := []PNG_Test{
PNG_Filter_Tests := []Test{
/*
PngSuite - Image filtering:
@@ -838,7 +839,7 @@ PNG_Filter_Tests := []PNG_Test{
},
}
PNG_Varied_IDAT_Tests := []PNG_Test{
PNG_Varied_IDAT_Tests := []Test{
/*
PngSuite - Chunk ordering:
@@ -897,7 +898,7 @@ PNG_Varied_IDAT_Tests := []PNG_Test{
},
}
PNG_ZLIB_Levels_Tests := []PNG_Test{
PNG_ZLIB_Levels_Tests := []Test{
/*
PngSuite - Zlib compression:
@@ -938,7 +939,7 @@ PNG_ZLIB_Levels_Tests := []PNG_Test{
},
}
PNG_sPAL_Tests := []PNG_Test{
PNG_sPAL_Tests := []Test{
/*
PngSuite - Additional palettes:
@@ -985,7 +986,7 @@ PNG_sPAL_Tests := []PNG_Test{
},
}
PNG_Ancillary_Tests := []PNG_Test{
PNG_Ancillary_Tests := []Test{
/*
PngSuite" - Ancillary chunks:
@@ -1153,7 +1154,7 @@ PNG_Ancillary_Tests := []PNG_Test{
}
Corrupt_PNG_Tests := []PNG_Test{
Corrupt_PNG_Tests := []Test{
/*
PngSuite - Corrupted files / PNG-files:
@@ -1249,7 +1250,7 @@ Corrupt_PNG_Tests := []PNG_Test{
}
No_Postprocesing_Tests := []PNG_Test{
No_Postprocesing_Tests := []Test{
/*
These are some custom tests where we skip expanding to RGB(A).
*/
@@ -1273,8 +1274,6 @@ No_Postprocesing_Tests := []PNG_Test{
},
}
Text_Title :: "PngSuite"
Text_Software :: "Created on a NeXTstation color using \"pnmtopng\"."
Text_Descrption :: "A compilation of a set of images created to test the\nvarious color-types of the PNG format. Included are\nblack&white, color, paletted, with alpha channel, with\ntransparency formats. All bit-depths allowed according\nto the spec are present."
@@ -1453,9 +1452,9 @@ png_test_no_postproc :: proc(t: ^testing.T) {
run_png_suite(t, No_Postprocesing_Tests)
}
run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
run_png_suite :: proc(t: ^testing.T, suite: []Test) {
for file in suite {
test_file := strings.concatenate({TEST_SUITE_PATH, file.file, ".png"})
test_file := strings.concatenate({TEST_SUITE_PATH_PNG, "/", file.file, ".png"}, context.allocator)
defer delete(test_file)
img: ^png.Image
@@ -1468,20 +1467,19 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
img, err = png.load(test_file, test.options)
passed := (test.expected_error == nil && err == nil) || (test.expected_error == err)
testing.expectf(t, passed, "%v failed with %v.", test_file, err)
testing.expectf(t, passed, "%q failed to load with error %v.", file.file, err)
if err == nil { // No point in running the other tests if it didn't load.
pixels := bytes.buffer_to_bytes(&img.pixels)
// This struct compare fails at -opt:2 if PNG_Dims is not #packed.
dims := PNG_Dims{img.width, img.height, img.channels, img.depth}
dims := Dims{img.width, img.height, img.channels, img.depth}
dims_pass := test.dims == dims
testing.expectf(t, dims_pass, "%v has %v, expected: %v.", file.file, dims, test.dims)
testing.expectf(t, dims_pass, "%v has %v, expected: %v", file.file, dims, test.dims)
passed &= dims_pass
png_hash := hash.crc32(pixels)
testing.expectf(t, test.hash == png_hash, "%v test %v hash is %08x, expected %08x with %v.", file.file, count, png_hash, test.hash, test.options)
testing.expectf(t, test.hash == png_hash, "%v test %v hash is %08x, expected %08x with %v", file.file, count, png_hash, test.hash, test.options)
passed &= test.hash == png_hash
@@ -1492,16 +1490,16 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
defer bytes.buffer_destroy(&qoi_buffer)
qoi_save_err := qoi.save(&qoi_buffer, img)
testing.expectf(t, qoi_save_err == nil, "%v test %v QOI save failed with %v.", file.file, count, qoi_save_err)
testing.expectf(t, qoi_save_err == nil, "%v test %v QOI save failed with %v", file.file, count, qoi_save_err)
if qoi_save_err == nil {
qoi_img, qoi_load_err := qoi.load(qoi_buffer.buf[:])
defer qoi.destroy(qoi_img)
testing.expectf(t, qoi_load_err == nil, "%v test %v QOI load failed with %v.", file.file, count, qoi_load_err)
testing.expectf(t, qoi_load_err == nil, "%v test %v QOI load failed with %v", file.file, count, qoi_load_err)
qoi_hash := hash.crc32(qoi_img.pixels.buf[:])
testing.expectf(t, qoi_hash == png_hash, "%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)
testing.expectf(t, qoi_hash == png_hash, "%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)
}
}
@@ -1511,15 +1509,15 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
defer bytes.buffer_destroy(&tga_buffer)
tga_save_err := tga.save(&tga_buffer, img)
testing.expectf(t, tga_save_err == nil, "%v test %v TGA save failed with %v.", file.file, count, tga_save_err)
testing.expectf(t, tga_save_err == nil, "%v test %v TGA save failed with %v", file.file, count, tga_save_err)
if tga_save_err == nil {
tga_img, tga_load_err := tga.load(tga_buffer.buf[:])
defer tga.destroy(tga_img)
testing.expectf(t, tga_load_err == nil, "%v test %v TGA load failed with %v.", file.file, count, tga_load_err)
testing.expectf(t, tga_load_err == nil, "%v test %v TGA load failed with %v", file.file, count, tga_load_err)
tga_hash := hash.crc32(tga_img.pixels.buf[:])
testing.expectf(t, tga_hash == png_hash, "%v test %v TGA load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, tga_hash, png_hash, test.options)
testing.expectf(t, tga_hash == png_hash, "%v test %v TGA load hash is %08x, expected it match PNG's %08x with %v", file.file, count, tga_hash, png_hash, test.options)
}
}
@@ -1528,18 +1526,18 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
pbm_buf, pbm_save_err := pbm.save_to_buffer(img)
defer delete(pbm_buf)
testing.expectf(t, pbm_save_err == nil, "%v test %v PBM save failed with %v.", file.file, count, pbm_save_err)
testing.expectf(t, pbm_save_err == nil, "%v test %v PBM save failed with %v", file.file, count, pbm_save_err)
if pbm_save_err == nil {
// Try to load it again.
pbm_img, pbm_load_err := pbm.load(pbm_buf)
defer pbm.destroy(pbm_img)
testing.expectf(t, pbm_load_err == nil, "%v test %v PBM load failed with %v.", file.file, count, pbm_load_err)
testing.expectf(t, pbm_load_err == nil, "%v test %v PBM load failed with %v", file.file, count, pbm_load_err)
if pbm_load_err == nil {
pbm_hash := hash.crc32(pbm_img.pixels.buf[:])
testing.expectf(t, pbm_hash == png_hash, "%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, pbm_hash, png_hash, test.options)
testing.expectf(t, pbm_hash == png_hash, "%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v", file.file, count, pbm_hash, png_hash, test.options)
}
}
}
@@ -1553,18 +1551,18 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
pbm_buf, pbm_save_err := pbm.save_to_buffer(img, pbm_info)
defer delete(pbm_buf)
testing.expectf(t, pbm_save_err == nil, "%v test %v PBM save failed with %v.", file.file, count, pbm_save_err)
testing.expectf(t, pbm_save_err == nil, "%v test %v PBM save failed with %v", file.file, count, pbm_save_err)
if pbm_save_err == nil {
// Try to load it again.
pbm_img, pbm_load_err := pbm.load(pbm_buf)
defer pbm.destroy(pbm_img)
testing.expectf(t, pbm_load_err == nil, "%v test %v PBM load failed with %v.", file.file, count, pbm_load_err)
testing.expectf(t, pbm_load_err == nil, "%v test %v PBM load failed with %v", file.file, count, pbm_load_err)
if pbm_load_err == nil {
pbm_hash := hash.crc32(pbm_img.pixels.buf[:])
testing.expectf(t, pbm_hash == png_hash, "%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, pbm_hash, png_hash, test.options)
testing.expectf(t, pbm_hash == png_hash, "%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v", file.file, count, pbm_hash, png_hash, test.options)
}
}
}
@@ -1653,7 +1651,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
case "pp0n2c16", "pp0n6a08":
gamma, gamma_ok := png.gamma(c)
expected_gamma := f32(1.0)
testing.expectf(t, gamma == expected_gamma && gamma_ok, "%v test %v gAMA is %v, expected %v.", file.file, count, gamma, expected_gamma)
testing.expectf(t, gamma == expected_gamma && gamma_ok, "%v test %v gAMA is %v, expected %v", file.file, count, gamma, expected_gamma)
}
case .PLTE:
switch(file.file) {
@@ -1661,7 +1659,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
plte, plte_ok := png.plte(c)
expected_plte_len := u16(216)
testing.expectf(t, expected_plte_len == plte.used && plte_ok, "%v test %v PLTE length is %v, expected %v.", file.file, count, plte.used, expected_plte_len)
testing.expectf(t, expected_plte_len == plte.used && plte_ok, "%v test %v PLTE length is %v, expected %v", file.file, count, plte.used, expected_plte_len)
}
case .sPLT:
switch(file.file) {
@@ -1669,10 +1667,10 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
splt, splt_ok := png.splt(c)
expected_splt_len := u16(216)
testing.expectf(t, expected_splt_len == splt.used && splt_ok, "%v test %v sPLT length is %v, expected %v.", file.file, count, splt.used, expected_splt_len)
testing.expectf(t, expected_splt_len == splt.used && splt_ok, "%v test %v sPLT length is %v, expected %v", file.file, count, splt.used, expected_splt_len)
expected_splt_name := "six-cube"
testing.expectf(t, expected_splt_name == splt.name && splt_ok, "%v test %v sPLT name is %v, expected %v.", file.file, count, splt.name, expected_splt_name)
testing.expectf(t, expected_splt_name == splt.name && splt_ok, "%v test %v sPLT name is %v, expected %v", file.file, count, splt.name, expected_splt_name)
png.splt_destroy(splt)
}
@@ -1686,31 +1684,31 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
g = png.CIE_1931{x = 0.3000, y = 0.6000},
b = png.CIE_1931{x = 0.1500, y = 0.0600},
}
testing.expectf(t, expected_chrm == chrm && chrm_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, chrm, expected_chrm)
testing.expectf(t, expected_chrm == chrm && chrm_ok, "%v test %v cHRM is %v, expected %v", file.file, count, chrm, expected_chrm)
}
case .pHYs:
phys, phys_ok := png.phys(c)
switch (file.file) {
case "cdfn2c08":
expected_phys := png.pHYs{ppu_x = 1, ppu_y = 4, unit = .Unknown}
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys)
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v", file.file, count, phys, expected_phys)
case "cdhn2c08":
expected_phys := png.pHYs{ppu_x = 4, ppu_y = 1, unit = .Unknown}
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys)
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v", file.file, count, phys, expected_phys)
case "cdsn2c08":
expected_phys := png.pHYs{ppu_x = 1, ppu_y = 1, unit = .Unknown}
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys)
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v", file.file, count, phys, expected_phys)
case "cdun2c08":
expected_phys := png.pHYs{ppu_x = 1000, ppu_y = 1000, unit = .Meter}
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys)
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v", file.file, count, phys, expected_phys)
}
case .hIST:
hist, hist_ok := png.hist(c)
switch (file.file) {
case "ch1n3p04":
testing.expectf(t, hist.used == 15 && hist_ok, "%v test %v hIST has %v entries, expected %v.", file.file, count, hist.used, 15)
testing.expectf(t, hist.used == 15 && hist_ok, "%v test %v hIST has %v entries, expected %v", file.file, count, hist.used, 15)
case "ch2n3p08":
testing.expectf(t, hist.used == 256 && hist_ok, "%v test %v hIST has %v entries, expected %v.", file.file, count, hist.used, 256)
testing.expectf(t, hist.used == 256 && hist_ok, "%v test %v hIST has %v entries, expected %v", file.file, count, hist.used, 256)
}
case .tIME:
png_time, png_time_ok := png.time(c)
@@ -1731,8 +1729,8 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
expected_core = time.Time{_nsec = 946684799000000000}
}
testing.expectf(t, png_time == expected_time && png_time_ok, "%v test %v tIME was %v, expected %v.", file.file, count, png_time, expected_time)
testing.expectf(t, core_time == expected_core && core_time_ok, "%v test %v tIME->core:time is %v, expected %v.", file.file, count, core_time, expected_core)
testing.expectf(t, png_time == expected_time && png_time_ok, "%v test %v tIME was %v, expected %v", file.file, count, png_time, expected_time)
testing.expectf(t, core_time == expected_core && core_time_ok, "%v test %v tIME->core:time is %v, expected %v", file.file, count, core_time, expected_core)
case .sBIT:
sbit, sbit_ok := png.sbit(c)
expected_sbit: [4]u8
@@ -1753,7 +1751,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
case "cdfn2c08", "cdhn2c08", "cdsn2c08", "cdun2c08", "ch1n3p04", "basn3p04":
expected_sbit = [4]u8{ 4, 4, 4, 0}
}
testing.expectf(t, sbit == expected_sbit && sbit_ok, "%v test %v sBIT was %v, expected %v.", file.file, count, sbit, expected_sbit)
testing.expectf(t, sbit == expected_sbit && sbit_ok, "%v test %v sBIT was %v, expected %v", file.file, count, sbit, expected_sbit)
case .tEXt, .zTXt:
text, text_ok := png.text(c)
defer png.text_destroy(text)
@@ -1765,7 +1763,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] {
test_text := Expected_Text[file.file][text.keyword].text
testing.expectf(t, text.text == test_text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text.text, test_text)
testing.expectf(t, text.text == test_text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text.text, test_text)
}
}
}
@@ -1778,44 +1776,44 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword]
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
}
}
case "ctfn0g04": // international UTF-8, finnish
if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword]
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
}
}
case "ctgn0g04": // international UTF-8, greek
if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword]
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
}
}
case "cthn0g04": // international UTF-8, hindi
if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword]
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
}
}
case "ctjn0g04": // international UTF-8, japanese
if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword]
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
}
}
}
@@ -1823,7 +1821,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
if file.file == "exif2c08" { // chunk with jpeg exif data
exif, exif_ok := png.exif(c)
testing.expectf(t, exif.byte_order == .big_endian && exif_ok, "%v test %v eXIf byte order '%v', expected 'big_endian'.", file.file, count, exif.byte_order)
testing.expectf(t, len(exif.data) == 978 && exif_ok, "%v test %v eXIf data length '%v', expected '%v'.", file.file, len(exif.data), 978)
testing.expectf(t, len(exif.data) == 978 && exif_ok, "%v test %v eXIf data length '%v', expected '%v'", file.file, len(exif.data), 978)
}
}
}
@@ -1833,4 +1831,516 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
png.destroy(img)
}
}
return
}
/*
Basic format tests:
https://entropymine.com/jason/bmpsuite/bmpsuite/html/bmpsuite.html - Version 2.8; 2023-11-28
The BMP Suite image generator itself is GPL, and isn't included, nor did it have its code referenced.
We do thank the author for the well-researched test suite, which we are free to include:
"Image files generated by this program are not covered by this license, and are
in the public domain (except for the embedded ICC profiles)."
The files with embedded ICC profiles aren't part of Odin's test assets. We don't support BMP metadata.
We don't support all "possibly correct" images, and thus only ship a subset of these from the BMP Suite.
*/
Basic_BMP_Tests := []Test{
{
"pal1", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"pal1wb", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"pal1bg", {
{Default, nil, {127, 64, 3, 8}, 0x_9e91_174a},
},
},
{
"pal4", {
{Default, nil, {127, 64, 3, 8}, 0x_288e_4371},
},
},
{
"pal4gs", {
{Default, nil, {127, 64, 3, 8}, 0x_452d_a01a},
},
},
{
"pal4rle", {
{Default, nil, {127, 64, 3, 8}, 0x_288e_4371},
},
},
{
"pal8", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8-0", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8gs", {
{Default, nil, {127, 64, 3, 8}, 0x_09c2_7834},
},
},
{
"pal8rle", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8w126", {
{Default, nil, {126, 63, 3, 8}, 0x_bb66_4cda},
},
},
{
"pal8w125", {
{Default, nil, {125, 62, 3, 8}, 0x_3ab8_f7c5},
},
},
{
"pal8w124", {
{Default, nil, {124, 61, 3, 8}, 0x_b53e_e6c8},
},
},
{
"pal8topdown", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8nonsquare", {
{Default, nil, {127, 32, 3, 8}, 0x_8409_c689},
},
},
{
"pal8v4", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8v5", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"rgb16", {
{Default, nil, {127, 64, 3, 8}, 0x_8b6f_81a2},
},
},
{
"rgb16bfdef", {
{Default, nil, {127, 64, 3, 8}, 0x_8b6f_81a2},
},
},
{
"rgb16-565", {
{Default, nil, {127, 64, 3, 8}, 0x_8c73_a2ff},
},
},
{
"rgb16-565pal", {
{Default, nil, {127, 64, 3, 8}, 0x_8c73_a2ff},
},
},
{
"rgb24", {
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgb24pal", {
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgb32", {
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgb32bf", {
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgb32bfdef", {
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
}
OS2_Tests := []Test{
{
"pal8os2", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2-sz", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2-hs", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2sp", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2v2", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2v2-16", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2v2-sz", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2v2-40sz", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
}
// BMP files that aren't 100% to spec. Some we support, some we don't.
Questionable_BMP_Tests := []Test{
{
"pal1p1", { // Spec says 1-bit image has 2 palette entries. This one has 1.
{Default, nil, {127, 64, 3, 8}, 0x_2b54_2560},
},
},
{
"pal2", { // 2-bit. Allowed on Windows CE. Irfanview doesn't support it.
{Default, nil, {127, 64, 3, 8}, 0x_0da2_7594},
},
},
{
"pal2color", { // 2-bit, with color palette.
{Default, nil, {127, 64, 3, 8}, 0x_f0d8_c5d6},
},
},
{
"pal8offs", { // 300 palette entries (yes, only 256 can be used)
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8oversizepal", { // Some padding between palette and image data
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal4rletrns", { // Using palette tricks to skip pixels
{Default, nil, {127, 64, 3, 8}, 0x_eed4_e744},
},
},
{
"pal4rlecut", { // Using palette tricks to skip pixels
{Default, nil, {127, 64, 3, 8}, 0x_473fbc7d},
},
},
{
"pal8rletrns", { // Using palette tricks to skip pixels
{Default, nil, {127, 64, 3, 8}, 0x_fe1f_e560},
},
},
{
"pal8rlecut", { // Using palette tricks to skip pixels
{Default, nil, {127, 64, 3, 8}, 0x_bd04_3619},
},
},
{
"rgb16faketrns", { // Using palette tricks to skip pixels
{Default, nil, {127, 64, 3, 8}, 0x_8b6f_81a2},
},
},
{
"rgb16-231", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_7393_a163},
},
},
{
"rgb16-3103", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_3b66_2189},
},
},
{
"rgba16-4444", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_b785_1f9f},
},
},
{
"rgba16-5551", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_8b6f_81a2},
},
},
{
"rgba16-1924", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_f038_2bed},
},
},
{
"rgb32-xbgr", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgb32fakealpha", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgb32-111110", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_b2c7_a8ff},
},
},
{
"rgb32-7187", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_b93a_4291},
},
},
{
"rgba32-1", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_7b67_823d},
},
},
{
"rgba32-2", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_7b67_823d},
},
},
{
"rgba32-1010102", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_aa42_0b16},
},
},
{
"rgba32-81284", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_28a2_4c16},
},
},
{
"rgba32-61754", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_4aae_26ed},
},
},
{
"rgba32abf", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_7b67_823d},
},
},
{
"rgb32h52", { // Truncated header (RGB bit fields included)
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgba32h56", { // Truncated header (RGBA bit fields included)
{Default, nil, {127, 64, 3, 8}, 0x_7b67_823d},
},
},
}
// Unsupported BMP features, or malformed images.
Unsupported_BMP_Tests := []Test{
{
"ba-bm", { // An OS/2 Bitmap array. We don't support this BA format.
{Default, .Unsupported_OS2_File, {127, 32, 3, 8}, 0x_0000_0000},
},
},
{
"pal1huffmsb", { // An OS/2 file with Huffman 1D compression
{Default, .Unsupported_Compression, {127, 32, 3, 8}, 0x_0000_0000},
},
},
{
"rgb24rle24", { // An OS/2 file with RLE24 compression
{Default, .Unsupported_Compression, {127, 64, 3, 8}, 0x_0000_0000},
},
},
{
"rgba64", { // An OS/2 file with RLE24 compression
{Default, .Unsupported_BPP, {127, 64, 3, 8}, 0x_0000_0000},
},
},
}
// Malformed / malicious files
Known_Bad_BMP_Tests := []Test{
{
"badbitcount", {
{Default, .Unsupported_BPP, {127, 64, 3, 8}, 0x_3ce81fae},
},
},
{
"badbitssize", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"baddens1", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"baddens2", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"badfilesize", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"badheadersize", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"badpalettesize", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"badplanes", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"badrle", {
{Default, nil, {127, 64, 3, 8}, 0x_1457_aae4},
},
},
{
"badrle4", {
{Default, nil, {127, 64, 3, 8}, 0x_6764_d2ac},
},
},
{
"badrle4bis", {
{Default, nil, {127, 64, 3, 8}, 0x_935d_bb37},
},
},
{
"badrle4ter", {
{Default, nil, {127, 64, 3, 8}, 0x_f2ba_5b08},
},
},
{
"badrlebis", {
{Default, nil, {127, 64, 3, 8}, 0x_07e2_d730},
},
},
{
"badrleter", {
{Default, nil, {127, 64, 3, 8}, 0x_a874_2742},
},
},
{
"badwidth", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"pal8badindex", {
{Default, nil, {127, 64, 3, 8}, 0x_0450_0d02},
},
},
{
"reallybig", {
{Default, .Image_Dimensions_Too_Large, {3000000, 2000000, 1, 24}, 0x_0000_0000},
},
},
{
"rgb16-880", {
{Default, nil, {127, 64, 3, 8}, 0x_f1c2_0c73},
},
},
{
"rletopdown", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"shortfile", {
{Default, .Short_Buffer, {127, 64, 1, 1}, 0x_0000_0000},
},
},
}
@test
bmp_test_basic :: proc(t: ^testing.T) {
run_bmp_suite(t, Basic_BMP_Tests)
}
@test
bmp_test_os2 :: proc(t: ^testing.T) {
run_bmp_suite(t, OS2_Tests)
}
@test
bmp_test_questionable :: proc(t: ^testing.T) {
run_bmp_suite(t, Questionable_BMP_Tests)
}
@test
bmp_test_unsupported :: proc(t: ^testing.T) {
run_bmp_suite(t, Unsupported_BMP_Tests)
}
@test
bmp_test_known_bad :: proc(t: ^testing.T) {
run_bmp_suite(t, Known_Bad_BMP_Tests)
}
run_bmp_suite :: proc(t: ^testing.T, suite: []Test) {
for file in suite {
test_file := strings.concatenate({TEST_SUITE_PATH_BMP, "/", file.file, ".bmp"}, context.allocator)
defer delete(test_file)
for test in file.tests {
img, err := bmp.load(test_file, test.options)
passed := (test.expected_error == nil && err == nil) || (test.expected_error == err)
testing.expectf(t, passed, "%q failed to load with error %v.", file.file, err)
if err == nil { // No point in running the other tests if it didn't load.
qoi_file := strings.concatenate({TEST_SUITE_PATH_BMP, "/", file.file, ".qoi"}, context.allocator)
defer delete(qoi_file)
qoi.save(qoi_file, img)
pixels := bytes.buffer_to_bytes(&img.pixels)
dims := Dims{img.width, img.height, img.channels, img.depth}
testing.expectf(t, test.dims == dims, "%v has %v, expected: %v.", file.file, dims, test.dims)
img_hash := hash.crc32(pixels)
testing.expectf(t, test.hash == img_hash, "%v test #1's hash is %08x, expected %08x with %v.", file.file, img_hash, test.hash, test.options)
}
bmp.destroy(img)
}
}
return
}