vlib/image/codecs_test.v
Rama master · réplica de lectura del repositorio original.
import encoding.hex
import image
import image.color
import image.draw
import image.jpeg
import image.png
import io
import os
struct CodecReader {
bytes []u8
mut:
pos int
}
fn (mut r CodecReader) read(mut buf []u8) !int {
if r.pos >= r.bytes.len {
return io.Eof{}
}
n := copy(mut buf, r.bytes[r.pos..])
r.pos += n
return n
}
struct CodecWriter {
mut:
bytes []u8
}
fn (mut w CodecWriter) write(bytes []u8) !int {
// Exercise partial writes, which file and network writers may return.
n := if bytes.len > 7 { 7 } else { bytes.len }
w.bytes << bytes[..n]
return n
}
struct FailingCodecReader {
bytes []u8
fail_at int
mut:
pos int
failed bool
}
fn (mut r FailingCodecReader) read(mut buf []u8) !int {
if r.pos == r.fail_at && !r.failed {
r.failed = true
return error('codec reader failure')
}
if r.pos >= r.bytes.len {
return io.Eof{}
}
end := if r.pos < r.fail_at { r.fail_at } else { r.bytes.len }
n := copy(mut buf, r.bytes[r.pos..end])
r.pos += n
return n
}
struct FailingCodecWriter {
zero_count bool
mut:
calls int
}
fn (mut w FailingCodecWriter) write(_ []u8) !int {
w.calls++
if w.calls == 1 {
return 1
}
if w.zero_count {
return 0
}
return error('codec writer failure')
}
struct OversizedPngImage {}
fn (img OversizedPngImage) color_model() color.Model {
return color.nrgba_model
}
fn (img OversizedPngImage) bounds() image.Rectangle {
// The pixel buffer would fit an i32, but adding PNG row filter bytes would not.
return image.rect(0, 0, 1, max_i32 / 4)
}
fn (img OversizedPngImage) at(_ int, _ int) color.Color {
panic('PNG dimensions must be rejected before accessing pixels')
}
fn test_registered_codecs_propagate_reader_errors() {
src := image.new_nrgba(image.rect(0, 0, 1, 1))
bytes := png.encode_to_bytes(src)!
// Exercise failures during format sniffing and after the buffered signature is consumed.
for fail_at in [0, 8] {
if _, _ := image.decode(FailingCodecReader{ bytes: bytes, fail_at: fail_at }) {
assert false, 'registered decode swallowed a reader error'
} else {
assert err.msg() == 'codec reader failure'
}
if _, _ := image.decode_config(FailingCodecReader{ bytes: bytes, fail_at: fail_at }) {
assert false, 'registered decode_config swallowed a reader error'
} else {
assert err.msg() == 'codec reader failure'
}
}
}
fn test_codecs_decode_files() {
src := image.new_nrgba(image.rect(0, 0, 1, 1))
for format in ['png', 'jpeg'] {
bytes := if format == 'png' {
png.encode_to_bytes(src)!
} else {
jpeg.encode_to_bytes(src)!
}
path := os.join_path(os.temp_dir(), 'v_image_codec_${format}_${os.getpid()}')
os.write_file_array(path, bytes)!
defer { os.rm(path) or {} }
mut file := os.open(path)!
decoded, name := image.decode(file)!
file.close()
assert name == format
assert decoded.bounds() == src.bounds()
file = os.open(path)!
config, config_name := image.decode_config(file)!
file.close()
assert config_name == format
assert config.width == 1
assert config.height == 1
}
}
fn test_codecs_propagate_writer_errors_and_reject_zero_writes() {
src := image.new_nrgba(image.rect(0, 0, 1, 1))
for zero_count in [false, true] {
mut png_writer := FailingCodecWriter{ zero_count: zero_count }
if _ := png.encode(mut png_writer, src) {
assert false, 'PNG encoder swallowed a writer failure'
} else {
assert err.msg() == if zero_count {
'image: invalid writer byte count'
} else {
'codec writer failure'
}
}
mut jpeg_writer := FailingCodecWriter{ zero_count: zero_count }
if _ := jpeg.encode(mut jpeg_writer, src) {
assert false, 'JPEG encoder swallowed a writer failure'
} else {
assert err.msg() == if zero_count {
'image: invalid writer byte count'
} else {
'codec writer failure'
}
}
}
}
fn test_png_rejects_filter_buffer_overflow_before_allocating() {
if _ := png.encode_to_bytes(OversizedPngImage{}) {
assert false, 'PNG encoder accepted an overflowing filter buffer'
} else {
assert err.msg() == 'image: invalid or oversized image dimensions'
}
}
fn test_png_external_fixture_and_registration() {
bytes := hex.decode('89504e470d0a1a0a0000000d49484452000000010000000108060000001f15c4890000000d49444154789c63f8cfc0f01f00050001ff89993d1d0000000049454e44ae426082')!
img, format := image.decode(CodecReader{ bytes: bytes })!
assert format == 'png'
assert img.bounds() == image.rect(0, 0, 1, 1)
assert color.to_nrgba(img.at(0, 0)) == color.NRGBA{255, 0, 0, 255}
config, config_format := image.decode_config(CodecReader{ bytes: bytes })!
assert config_format == 'png'
assert config.width == 1
assert config.height == 1
}
fn test_png_rejects_apple_cgbi_images() {
// Independently encoded premultiplied BGRA fixtures: opaque and half-alpha red.
for fixture in [
'89504e470d0a1a0a0000000443674249500020022bd5b37f0000000d49484452000000010000000108060000001f15c4890000000749444154636060f8ff1f0052def1700000000049454e44ae426082',
'89504e470d0a1a0a0000000443674249500020022bd5b37f0000000d49484452000000010000000108060000001f15c489000000074944415463606068680000da98b03f0000000049454e44ae426082',
] {
bytes := hex.decode(fixture)!
if _ := png.decode(CodecReader{ bytes: bytes }) {
assert false, 'PNG decoder accepted premultiplied BGRA pixels'
} else {
assert err.msg() == 'image: Apple CgBI PNG is not supported'
}
if _ := png.decode_config(CodecReader{ bytes: bytes }) {
assert false, 'PNG configuration accepted an unsupported format'
} else {
assert err.msg() == 'image: Apple CgBI PNG is not supported'
}
if _, _ := image.decode(CodecReader{ bytes: bytes }) {
assert false, 'registered PNG decoder accepted premultiplied BGRA pixels'
} else {
assert err.msg() == 'image: Apple CgBI PNG is not supported'
}
if _, _ := image.decode_config(CodecReader{ bytes: bytes }) {
assert false, 'registered PNG configuration accepted an unsupported format'
} else {
assert err.msg() == 'image: Apple CgBI PNG is not supported'
}
}
}
fn test_png_accepts_cgbi_text_in_ordinary_chunk_data() {
// "CgBI" in ancillary data is not a CgBI chunk. stb does not check chunk CRCs.
bytes := hex.decode('89504e470d0a1a0a0000000d49484452000000010000000108060000001f15c48900000009744558746e6f74650043674249000000000000000d49444154789c63f8cfc0f01f00050001ff89993d1d0000000049454e44ae426082')!
img, format := image.decode(CodecReader{ bytes: bytes })!
assert format == 'png'
assert color.to_nrgba(img.at(0, 0)) == color.NRGBA{255, 0, 0, 255}
config := png.decode_config(CodecReader{ bytes: bytes })!
assert config.width == 1
assert config.height == 1
}
fn test_png_preserves_alpha_and_source_bounds() {
mut src := image.new_nrgba(image.rect(5, 7, 7, 8))
src.set_nrgba(5, 7, color.NRGBA{120, 60, 30, 128})
src.set_nrgba(6, 7, color.NRGBA{10, 20, 30, 255})
mut writer := CodecWriter{}
png.encode(mut writer, src)!
decoded := png.decode(CodecReader{ bytes: writer.bytes })!
assert decoded.bounds() == image.rect(0, 0, 2, 1)
assert color.to_nrgba(decoded.at(0, 0)) == src.nrgba_at(5, 7)
assert color.to_nrgba(decoded.at(1, 0)) == src.nrgba_at(6, 7)
config := png.decode_config(CodecReader{ bytes: writer.bytes })!
assert config.width == 2
assert config.height == 1
}
fn test_resize_then_jpeg_encode_and_registered_decode() {
mut src := image.new_nrgba(image.rect(0, 0, 4, 4))
for y in 0 .. 4 {
for x in 0 .. 4 {
src.set_nrgba(x, y, color.NRGBA{240, 30, 10, 255})
}
}
resized := draw.resize(src, 2, 3)!
mut writer := CodecWriter{}
jpeg.encode(mut writer, resized, quality: 95)!
img, format := image.decode(CodecReader{ bytes: writer.bytes })!
assert format == 'jpeg'
assert img.bounds() == image.rect(0, 0, 2, 3)
c := color.to_nrgba(img.at(0, 0))
assert c.r >= 235 && c.r <= 245
assert c.g >= 25 && c.g <= 35
assert c.b >= 5 && c.b <= 15
assert c.a == 255
config, name := image.decode_config(CodecReader{ bytes: writer.bytes })!
assert name == 'jpeg'
assert config.width == 2
assert config.height == 3
}
fn test_codecs_reject_wrong_formats_and_invalid_quality() {
mut src := image.new_rgba(image.rect(0, 0, 1, 1))
bytes := png.encode_to_bytes(src)!
if _ := jpeg.decode(CodecReader{ bytes: bytes }) {
assert false, 'JPEG decoder accepted a PNG'
}
if _ := png.decode(CodecReader{ bytes: 'not an image'.bytes() }) {
assert false, 'PNG decoder accepted an invalid signature'
}
for quality in [0, 101] {
if _ := jpeg.encode_to_bytes(src, quality: quality) {
assert false, 'accepted invalid JPEG quality'
}
}
if _ := png.encode_to_bytes(image.NRGBA{}) {
assert false, 'accepted an empty image'
}
}