Ctrl K
ComunidadGuías…

vlib/image/image_types_test.v

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
module image

import image.color

// A width of 4 and a height of 4, so `opaque` has to look at 16 pixels across
// more than one row to prove it walks the stride rather than the first row.
const types_rect = rect(1, 2, 5, 6)

fn opaque_uniform() color.Color {
	return color.Color(color.RGBA{
		r: 1
		g: 2
		b: 3
		a: 255
	})
}

fn test_uniform_constants_and_helpers() {
	br, bg, bb, ba := black.rgba()
	assert br == 0 && bg == 0 && bb == 0 && ba == 0xffff
	wr, wg, wb, wa := white.rgba()
	assert wr == 0xffff && wg == 0xffff && wb == 0xffff && wa == 0xffff
	tr, tg, tb, ta := transparent.rgba()
	assert tr == 0 && tg == 0 && tb == 0 && ta == 0
	or_, og, ob, oa := opaque.rgba()
	assert or_ == 0xffff && og == 0xffff && ob == 0xffff && oa == 0xffff

	u := new_uniform(opaque_uniform())
	r, g, b, a := u.rgba()
	assert r == 0x0101
	assert g == 0x0202
	assert b == 0x0303
	assert a == 0xffff
	assert u.rgba64_at(-7, 9) == color.RGBA64{
		r: 0x0101
		g: 0x0202
		b: 0x0303
		a: 0xffff
	}
	assert u.at(-100, 100) == opaque_uniform()
	assert u.at(0, 0) == opaque_uniform()
	assert u.convert(color.opaque) == opaque_uniform()
	assert u.opaque()
	assert !transparent.opaque()
	assert opaque.opaque()
	assert u.bounds() == rect(-1_000_000_000, -1_000_000_000, 1_000_000_000, 1_000_000_000)

	// The uniform model maps every input onto the uniform color.
	model := u.color_model()
	assert model.convert(color.opaque)! == opaque_uniform()
	assert model.convert(color.transparent)! == opaque_uniform()
	assert transparent.color_model().convert(color.opaque)! == color.transparent
}

fn test_uniform_color_model_of_the_named_constants() {
	assert black.color_model().convert(color.opaque)! == color.Color(color.Gray16{})
	assert white.color_model().convert(color.opaque)! == color.Color(color.Gray16{
		y: 0xffff
	})
}

fn test_rgba_out_of_bounds_is_a_no_op() {
	mut img := new_rgba(types_rect)
	assert img.rgba_at(0, 0) == color.RGBA{}
	assert img.rgba_at(100, 100) == color.RGBA{}
	assert img.at(0, 0) == color.Color(color.RGBA{})
	assert img.rgba64_at(0, 0) == color.RGBA64{}
	// A fresh image is zeroed, so every alpha channel is 0 and it is not opaque.
	assert !img.opaque()

	img.set(0, 0, color.opaque)
	img.set_rgba(100, 100, color.RGBA{
		r: 1
		g: 2
		b: 3
		a: 4
	})
	img.set_rgba64(100, 100, color.RGBA64{
		r: 1
		g: 2
		b: 3
		a: 4
	})
	assert img.pix[0] == 0
	assert img.pix.len == 64
}

fn test_rgba_opaque_walks_every_row() {
	mut img := new_rgba(types_rect)
	for y in types_rect.min.y .. types_rect.max.y {
		for x in types_rect.min.x .. types_rect.max.x {
			img.set_rgba(x, y, color.RGBA{
				r: x
				g: y
				b: 0
				a: 0xff
			})
		}
	}
	assert img.opaque()
	img.set_rgba(types_rect.max.x - 1, types_rect.max.y - 1, color.RGBA{
		r: 0
		g: 0
		b: 0
		a: 0xfe
	})
	assert !img.opaque()
}

fn test_rgba_sub_image_of_a_disjoint_rectangle_is_empty() {
	mut img := new_rgba(types_rect)
	img.set_rgba(1, 2, color.RGBA{
		r: 9
		g: 8
		b: 7
		a: 255
	})
	sub := img.sub_image(rect(100, 100, 110, 110))
	assert sub.rect == Rectangle{}
	assert sub.pix.len == 0
	assert sub.stride == 0
	assert sub.opaque()
}

fn test_empty_images_are_opaque() {
	assert new_rgba(Rectangle{}).opaque()
	assert new_rgba64(Rectangle{}).opaque()
	assert new_nrgba(Rectangle{}).opaque()
	assert new_nrgba64(Rectangle{}).opaque()
	assert new_alpha(Rectangle{}).opaque()
	assert new_alpha16(Rectangle{}).opaque()
	assert new_cmyk(Rectangle{}).opaque()
	assert new_gray(Rectangle{}).opaque()
	assert new_gray16(Rectangle{}).opaque()
	assert new_ycbcr(Rectangle{}, .ratio_420).opaque()
	assert new_nycbcra(Rectangle{}, .ratio_420).opaque()
	assert new_paletted(Rectangle{}, color.Palette{
		colors: [
			color.opaque,
		]
	}).opaque()
}

fn test_rgba64_stores_big_endian_channels() {
	mut img := new_rgba64(types_rect)
	assert img.rgba64_at(1, 2) == color.RGBA64{}
	assert img.at(9, 9) == color.Color(color.RGBA64{})

	img.set_rgba64(1, 2, color.RGBA64{
		r: 0x1234
		g: 0x5678
		b: 0x9abc
		a: 0xdef0
	})
	assert img.rgba64_at(1, 2) == color.RGBA64{
		r: 0x1234
		g: 0x5678
		b: 0x9abc
		a: 0xdef0
	}
	i := img.pix_offset(1, 2)
	assert img.pix[i] == 0x12
	assert img.pix[i + 1] == 0x34
	assert img.pix[i + 6] == 0xde
	assert img.pix[i + 7] == 0xf0

	sub := img.sub_image(rect(200, 200, 210, 210))
	assert sub.rect == Rectangle{}
	assert sub.pix.len == 0
}

fn test_rgba64_opaque_needs_both_bytes_of_alpha() {
	mut img := new_rgba64(types_rect)
	for y in types_rect.min.y .. types_rect.max.y {
		for x in types_rect.min.x .. types_rect.max.x {
			img.set_rgba64(x, y, color.RGBA64{
				r: 0
				g: 0
				b: 0
				a: 0xffff
			})
		}
	}
	assert img.opaque()

	// A single 0xfe00 in the high byte alone still reads as "not opaque".
	img.set_rgba64(types_rect.max.x - 1, types_rect.max.y - 1, color.RGBA64{
		a: 0xfffe
	})
	assert !img.opaque()
}

fn test_nrgba_unpremultiplies_when_storing_rgba64() {
	mut img := new_nrgba(types_rect)
	assert img.nrgba_at(0, 0) == color.NRGBA{}
	img.set_rgba64(1, 2, color.RGBA64{
		r: 0x8000
		g: 0x8000
		b: 0x8000
		a: 0x8000
	})
	assert img.nrgba_at(1, 2) == color.NRGBA{
		r: 0xff
		g: 0xff
		b: 0xff
		a: 0x80
	}
	assert img.rgba64_at(1, 2) == color.RGBA64{
		r: 0x8080
		g: 0x8080
		b: 0x8080
		a: 0x8080
	}
	assert !img.opaque()
	assert img.sub_image(Rectangle{}).rect == Rectangle{}
}

fn test_nrgba64_unpremultiplies_when_storing_rgba64() {
	mut img := new_nrgba64(types_rect)
	img.set_rgba64(1, 2, color.RGBA64{
		r: 0x4000
		g: 0x8000
		b: 0xc000
		a: 0x8000
	})
	// The un-premultiplied blue channel is 0x18002 and is truncated to u16.
	assert img.nrgba64_at(1, 2) == color.NRGBA64{
		r: 0x7fff
		g: 0xffff
		b: 0x7ffe
		a: 0x8000
	}
}

fn test_alpha_images() {
	mut img := new_alpha(types_rect)
	assert img.color_model() == color.alpha_model
	assert img.alpha_at(0, 0) == color.Alpha{}
	assert img.rgba64_at(0, 0) == color.RGBA64{}
	img.set_rgba64(1, 2, color.RGBA64{
		r: 0x1234
		g: 0x1234
		b: 0x1234
		a: 0x5678
	})
	assert img.alpha_at(1, 2) == color.Alpha{
		a: 0x56
	}
	assert img.rgba64_at(1, 2) == color.RGBA64{
		r: 0x5656
		g: 0x5656
		b: 0x5656
		a: 0x5656
	}
	assert !img.opaque()
	assert img.sub_image(Rectangle{}).rect == Rectangle{}
}

fn test_alpha16_images() {
	mut img := new_alpha16(types_rect)
	assert img.color_model() == color.alpha16_model
	assert img.alpha16_at(0, 0) == color.Alpha16{}
	img.set_alpha16(1, 2, color.Alpha16{
		a: 0xabcd
	})
	assert img.alpha16_at(1, 2) == color.Alpha16{
		a: 0xabcd
	}
	assert img.rgba64_at(1, 2) == color.RGBA64{
		r: 0xabcd
		g: 0xabcd
		b: 0xabcd
		a: 0xabcd
	}
	assert !img.opaque()
	assert img.sub_image(Rectangle{}).rect == Rectangle{}
}

fn test_gray_images_are_always_opaque() {
	mut img := new_gray(types_rect)
	assert img.color_model() == color.gray_model
	assert img.opaque()
	assert img.gray_at(9, 9) == color.Gray{}
	img.set_gray(1, 2, color.Gray{
		y: 0x80
	})
	assert img.gray_at(1, 2) == color.Gray{
		y: 0x80
	}
	assert img.rgba64_at(1, 2) == color.RGBA64{
		r: 0x8080
		g: 0x8080
		b: 0x8080
		a: 0xffff
	}
	assert img.opaque()
	assert img.sub_image(Rectangle{}).rect == Rectangle{}
}

fn test_gray16_images_are_always_opaque() {
	mut img := new_gray16(types_rect)
	assert img.color_model() == color.gray16_model
	assert img.opaque()
	assert img.gray16_at(9, 9) == color.Gray16{}
	assert img.rgba64_at(1, 2) == color.RGBA64{
		a: 0xffff
	}
	img.set_gray16(1, 2, color.Gray16{
		y: 0xbeef
	})
	assert img.gray16_at(1, 2) == color.Gray16{
		y: 0xbeef
	}
	assert img.opaque()
	assert img.sub_image(Rectangle{}).rect == Rectangle{}
}

fn test_cmyk_images() {
	mut img := new_cmyk(types_rect)
	assert img.color_model() == color.cmyk_model
	assert img.cmyk_at(0, 0) == color.CMYK{}
	// CMYK{0,0,0,0} is white: no ink on white paper.
	assert img.rgba64_at(0, 0) == color.RGBA64{
		r: 0xffff
		g: 0xffff
		b: 0xffff
		a: 0xffff
	}
	img.set_cmyk(1, 2, color.CMYK{
		c: 1
		m: 2
		y: 3
		k: 4
	})
	assert img.cmyk_at(1, 2) == color.CMYK{
		c: 1
		m: 2
		y: 3
		k: 4
	}
	assert img.rgba64_at(1, 2).a == 0xffff
	assert img.opaque()
	assert img.sub_image(Rectangle{}).rect == Rectangle{}
}

fn test_paletted_image_edges() {
	empty := new_paletted(types_rect, color.Palette{})
	assert empty.at(1, 2) == color.transparent
	assert empty.color_index_at(1, 2) == 0
	assert empty.color_index_at(100, 100) == 0
	assert empty.opaque()
	assert empty.sub_image(Rectangle{}).palette.colors.len == 0

	pal := color.Palette{
		colors: [
			color.opaque,
			color.transparent,
		]
	}
	mut img := new_paletted(types_rect, pal)
	assert img.color_model() == color.Model(pal)
	assert img.color_index_at(100, 100) == 0
	img.set_color_index(1, 2, 1)
	assert img.at(1, 2) == color.transparent
	assert img.color_index_at(1, 2) == 1
	assert !img.opaque()
	img.set_color_index(1, 2, 0)
	img.set_color_index(2, 2, 1)
	assert !img.opaque()
	img.set_color_index(2, 2, 0)
	// Every referenced palette entry is now opaque.
	for y in types_rect.min.y .. types_rect.max.y {
		for x in types_rect.min.x .. types_rect.max.x {
			img.set_color_index(x, y, 0)
		}
	}
	assert img.opaque()

	// set stores the nearest palette index.
	img.set(3, 3, color.opaque)
	assert img.color_index_at(3, 3) == 0
	img.set(3, 3, color.transparent)
	assert img.color_index_at(3, 3) == 1

	sub := img.sub_image(Rectangle{})
	assert sub.rect == Rectangle{}
	assert sub.palette.colors.len == 2
	assert sub.opaque()
}

fn test_ycbcr_subsample_ratio_names() {
	assert YCbCrSubsampleRatio.ratio_444.str() == 'YCbCrSubsampleRatio444'
	assert YCbCrSubsampleRatio.ratio_422.str() == 'YCbCrSubsampleRatio422'
	assert YCbCrSubsampleRatio.ratio_420.str() == 'YCbCrSubsampleRatio420'
	assert YCbCrSubsampleRatio.ratio_440.str() == 'YCbCrSubsampleRatio440'
	assert YCbCrSubsampleRatio.ratio_411.str() == 'YCbCrSubsampleRatio411'
	assert YCbCrSubsampleRatio.ratio_410.str() == 'YCbCrSubsampleRatio410'
}

// A 4x4 rectangle gives every ratio a non-trivial chroma plane, so the strides
// and buffer sizes are checked per ratio rather than only for the default.
fn test_ycbcr_buffer_layout_per_subsample_ratio() {
	mut sizes := map[string]int{}
	sizes['YCbCrSubsampleRatio444'] = 16
	sizes['YCbCrSubsampleRatio422'] = 12
	sizes['YCbCrSubsampleRatio420'] = 6
	sizes['YCbCrSubsampleRatio440'] = 8
	sizes['YCbCrSubsampleRatio411'] = 8
	sizes['YCbCrSubsampleRatio410'] = 4
	mut c_strides := map[string]int{}
	c_strides['YCbCrSubsampleRatio444'] = 4
	c_strides['YCbCrSubsampleRatio422'] = 3
	c_strides['YCbCrSubsampleRatio420'] = 3
	c_strides['YCbCrSubsampleRatio440'] = 4
	c_strides['YCbCrSubsampleRatio411'] = 2
	c_strides['YCbCrSubsampleRatio410'] = 2

	for ratio in [YCbCrSubsampleRatio.ratio_444, .ratio_422, .ratio_420, .ratio_440, .ratio_411,
		.ratio_410] {
		name := ratio.str()
		mut img := new_ycbcr(types_rect, ratio)
		assert img.y.len == 16
		assert img.y_stride == 4
		assert img.cb.len == sizes[name]
		assert img.cr.len == sizes[name]
		assert img.c_stride == c_strides[name]
		assert img.color_model() == color.ycbcr_model
		assert img.bounds() == types_rect
		assert img.opaque()
		assert img.ycbcr_at(0, 0) == color.YCbCr{}

		sub := img.sub_image(Rectangle{})
		assert sub.rect == Rectangle{}
		assert sub.subsample_ratio == ratio
		assert sub.c_stride == 0
		assert sub.y.len == 0

		mut with_alpha := new_nycbcra(types_rect, ratio)
		assert with_alpha.color_model() == color.nycbcra_model
		assert with_alpha.bounds() == types_rect
		assert with_alpha.a.len == 16
		assert with_alpha.a_stride == 4
		// types_rect.min is (1,2), so the alpha offset of the origin is 0.
		assert with_alpha.a_offset(1, 2) == 0
		assert with_alpha.a_offset(2, 2) == 1
		assert with_alpha.a_offset(1, 3) == 4
		assert with_alpha.nycbcra_at(0, 0) == color.NYCbCrA{}
		assert !with_alpha.opaque()
		alpha_sub := with_alpha.sub_image(Rectangle{})
		assert alpha_sub.ycbcr.rect == Rectangle{}
		assert alpha_sub.ycbcr.subsample_ratio == ratio
		assert alpha_sub.a.len == 0
	}
}

fn test_nycbcra_opaque_walks_the_alpha_plane() {
	mut img := new_nycbcra(types_rect, .ratio_444)
	for y in types_rect.min.y .. types_rect.max.y {
		for x in types_rect.min.x .. types_rect.max.x {
			img.a[img.a_offset(x, y)] = 0xff
		}
	}
	assert img.opaque()
	img.a[img.a_offset(types_rect.max.x - 1, types_rect.max.y - 1)] = 0xfe
	assert !img.opaque()
}