Files
pdf-odin-llm/bitmap_test.odin
2026-09-25 23:32:29 +02:00

129 lines
4.3 KiB
Odin

package pdf
import "core:testing"
@(test)
test_bitmap_make_and_bounds :: proc(t: ^testing.T) {
bmp, err := bitmap_make(4, 3, 4)
testing.expect_value(t, err, Error.None)
defer bitmap_destroy(&bmp)
testing.expect_value(t, bmp.stride, 16)
testing.expect_value(t, len(bmp.pixels), 48)
testing.expect_value(t, len(bitmap_row(&bmp, 0)), 16)
testing.expect_value(t, len(bitmap_row(&bmp, 2)), 16)
testing.expect_value(t, len(bitmap_row(&bmp, 3)), 0) // out of range
testing.expect_value(t, len(bitmap_row(&bmp, -1)), 0)
off, ok := bitmap_pixel_offset(&bmp, 3, 2)
testing.expect(t, ok, "in-range pixel")
testing.expect_value(t, off, 2 * 16 + 3 * 4)
_, ok2 := bitmap_pixel_offset(&bmp, 4, 0)
testing.expect(t, !ok2, "x == width is out of range")
}
@(test)
test_bitmap_rejects_bad_sizes :: proc(t: ^testing.T) {
_, e1 := bitmap_make(0, 10, 4)
testing.expect_value(t, e1, Error.Invalid_Argument)
_, e2 := bitmap_make(10, 10, 5)
testing.expect_value(t, e2, Error.Invalid_Argument)
_, e3 := bitmap_make(-1, 10, 4)
testing.expect_value(t, e3, Error.Invalid_Argument)
// A size whose product overflows must be refused, not wrapped.
_, e4 := bitmap_make(1 << 20, 1 << 20, 4)
testing.expect_value(t, e4, Error.Bitmap_Too_Large)
}
@(test)
test_bitmap_fill_and_clear :: proc(t: ^testing.T) {
bmp, _ := bitmap_make(2, 2, 4)
defer bitmap_destroy(&bmp)
bitmap_fill(&bmp, {1, 2, 3, 4})
testing.expect_value(t, bmp.pixels[0], 1)
testing.expect_value(t, bmp.pixels[3], 4)
testing.expect_value(t, bmp.pixels[len(bmp.pixels) - 1], 4)
bitmap_clear(&bmp, 9)
for b in bmp.pixels {
testing.expect_value(t, b, 9)
}
}
// Premultiply then unpremultiply must return the original colour for every
// alpha where the information survives the round trip.
@(test)
test_bitmap_premultiply_round_trip :: proc(t: ^testing.T) {
bmp, _ := bitmap_make(1, 1, 4)
defer bitmap_destroy(&bmp)
for a in ([]u8{255, 200, 128, 64, 1}) {
bmp.pixels[0] = 200
bmp.pixels[1] = 100
bmp.pixels[2] = 50
bmp.pixels[3] = a
bmp.premultiplied = false
bitmap_premultiply(&bmp)
testing.expect(t, bmp.premultiplied, "should be marked premultiplied")
testing.expect(t, bmp.pixels[0] <= 200, "premultiplied value cannot exceed the original")
bitmap_unpremultiply(&bmp)
testing.expect(t, !bmp.premultiplied, "should be marked straight")
// Rounding through 8 bits at low alpha is lossy; the tolerance scales
// with 255/alpha, which is the quantisation step of the round trip.
tol := int(255 / max(int(a), 1)) + 1
testing.expect(t, abs(int(bmp.pixels[0]) - 200) <= tol, "red survives the round trip")
testing.expect(t, abs(int(bmp.pixels[1]) - 100) <= tol, "green survives the round trip")
testing.expect_value(t, bmp.pixels[3], a)
}
}
// Fully transparent pixels lose their colour, deliberately: leaving the
// multiplied zeros in place makes bilinear filtering drag black into edges.
@(test)
test_bitmap_unpremultiply_zero_alpha :: proc(t: ^testing.T) {
bmp, _ := bitmap_make(1, 1, 4)
defer bitmap_destroy(&bmp)
bmp.pixels[0] = 77
bmp.pixels[1] = 88
bmp.pixels[2] = 99
bmp.pixels[3] = 0
bmp.premultiplied = true
bitmap_unpremultiply(&bmp)
testing.expect_value(t, bmp.pixels[0], 0)
testing.expect_value(t, bmp.pixels[1], 0)
testing.expect_value(t, bmp.pixels[2], 0)
testing.expect_value(t, bmp.pixels[3], 0)
}
@(test)
test_bitmap_to_rgba_expands :: proc(t: ^testing.T) {
grey, _ := bitmap_make(2, 1, 1)
defer bitmap_destroy(&grey)
grey.pixels[0] = 10
grey.pixels[1] = 20
rgba, err := bitmap_to_rgba(&grey)
testing.expect_value(t, err, Error.None)
defer bitmap_destroy(&rgba)
testing.expect_value(t, rgba.n, 4)
testing.expect_value(t, rgba.pixels[0], 10)
testing.expect_value(t, rgba.pixels[1], 10)
testing.expect_value(t, rgba.pixels[2], 10)
testing.expect_value(t, rgba.pixels[3], 255)
testing.expect_value(t, rgba.pixels[4], 20)
testing.expect_value(t, rgba.pixels[7], 255)
}
@(test)
test_bitmap_destroy_is_idempotent :: proc(t: ^testing.T) {
bmp, _ := bitmap_make(2, 2, 3)
bitmap_destroy(&bmp)
bitmap_destroy(&bmp)
testing.expect_value(t, len(bmp.pixels), 0)
}