Image codecs for MoonBit: PNG, JPEG, GIF, WebP, BMP, TGA, QOI, TIFF, ICO, PNM, Radiance HDR, OpenEXR, farbfeld and DDS, read and written, with the pixel arithmetic, filters and colour between them.
test {
let image = @moonimage.Image::new(640, 480, @moonimage.PixelFormat::Rgba8)
let small = image.scale(160, 120, sampling=@moonimage.Sampling::Bilinear)
let file = @moonimage.encode(small, @moonimage.Format::Png)
let back = @moonimage.decode(file)
assert_eq(back.width, 160)
assert_eq(back.height, 120)
}test {
let image = @moonimage.Image::new(8, 8, @moonimage.PixelFormat::Rgb8)
let file = @moonimage.encode(image, @moonimage.Format::Tiff)
let safe = @moonimage.decode(file, limits=@moonimage.Limits::strict())
assert_eq(safe.width, 8)
}test {
let refused = try {
ignore(@moonimage.decode(b"not an image"))
abort("unreachable")
} catch {
error => error
}
assert_eq(refused is @moonimage.Refused::Unknown(_), true)
}| Format | Read | Write | Animation | Package |
|---|---|---|---|---|
| PNG | every colour type and depth, palette and tRNS, Adam7, five filters, CRC, gAMA cHRM sRGB iCCP tEXt zTXt iTXt | 8 and 16 bits, palette when it is smaller, adaptive filtering, interlaced on request, metadata | APNG | @moonimage/png |
| QOI | all six opcodes | the reference encoder's choices, byte for byte | — | @moonimage/qoi |
| BMP | every header version, 1/4/8/16/24/32 bits, BI_RGB BI_RLE4 BI_RLE8 BI_BITFIELDS, both row orders | 24 and 32 bits | — | @moonimage/bmp |
| TGA | colour mapped, true colour and grey, run lengths, 8/15/16/24/32 bits, both row orders | 24 and 32 bits, run lengths on request | — | @moonimage/tga |
| JPEG | baseline and progressive, every subsampling, greyscale, EXIF orientation | baseline, quality 1–100, three subsamplings | — | @moonimage/jpeg |
| GIF | palettes, transparency, interlace, run lengths | palette by exact colours or median cut, dithering | yes | @moonimage/gif |
| WebP | lossless (VP8L) with its four transforms, and lossy (VP8) with its boolean coder, predictors, loop filter and chroma upsampling; alpha in either; animated files | lossless, with a predictor transform and LZ77 | yes | @moonimage/webp |
| TIFF | strips, LZW, PackBits, deflate or none, horizontal prediction, grey, grey with alpha, RGB, RGBA and palette at 1 to 16 bits, planar or chunky, every page | 8 and 16 bits, three compressions, prediction on request, several pages | pages | @moonimage/tiff |
| ICO / CUR | PNG entries and bitmap entries at 1/4/8/24/32 bits with the one-bit mask, every size in the file | PNG entries, at any list of sizes | sizes | @moonimage/ico |
| PNM | PBM, PGM, PPM and PAM, both spellings, any maximum | all four shapes, both spellings | — | @moonimage/pnm |
| Radiance HDR | RGBE, the new run-length code and the flat one, all four origins | RGBE with the run-length code | — | @moonimage/hdr |
| OpenEXR | scanlines in half or float, uncompressed, RLE, ZIPS or ZIP | the same three compressions | — | @moonimage/exr |
| farbfeld | the whole format | the whole format | — | @moonimage/farbfeld |
| DDS | BC1 to BC7 blocks, and uncompressed surfaces | — | — | @moonimage/dds |
pub(all) struct Image {
width : Int32
height : Int32
format : PixelFormat
stride : Int32
pixels : Bytes
premultiplied : Bool
metadata : Metadata
}| Pixel format | Samples | Bytes a pixel |
|---|---|---|
| Gray8, GrayAlpha8, Rgb8, Rgba8 | 8-bit | 1, 2, 3, 4 |
| Gray16, GrayAlpha16, Rgb16, Rgba16 | 16-bit, big-endian | 2, 4, 6, 8 |
| RgbaF32 | floats, little-endian | 16 |
test {
let image = @moonimage.Image::new(4, 2, @moonimage.PixelFormat::Rgba8)
let grey = image.convert(@moonimage.PixelFormat::Gray8)
let wide = image.convert(@moonimage.PixelFormat::Rgba16)
assert_eq(grey.format, @moonimage.PixelFormat::Gray8)
assert_eq(wide.format, @moonimage.PixelFormat::Rgba16)
let flat = image.to_premultiplied()
assert_eq(flat.premultiplied, true)
assert_eq(flat.to_straight().premultiplied, false)
}test {
let image = @moonimage.Image::new(3, 2, @moonimage.PixelFormat::Rgba8)
assert_eq(image.flip_horizontal().width, 3)
assert_eq(image.flip_vertical().height, 2)
assert_eq(image.rotate(quarters=1).width, 2)
assert_eq(image.crop(0, 0, 2, 1).height, 1)
}test {
let image = @moonimage.Image::new(9, 7, @moonimage.PixelFormat::Rgb8)
for
sampling in [
@moonimage.Sampling::Nearest,
@moonimage.Sampling::Bilinear,
@moonimage.Sampling::CatmullRom,
@moonimage.Sampling::Gaussian,
@moonimage.Sampling::Lanczos,
] {
assert_eq(image.scale(16, 12, sampling~).width, 16)
}
}test {
let image = @moonimage.Image::new(8, 4, @moonimage.PixelFormat::Rgba8)
let tilted = image.rotate_by(30.0, expand=true)
assert_eq(tilted.width > 8, true)
assert_eq(image.rotate_by(90.0).width, 4)
}test {
let image = @moonimage.Image::new(6, 5, @moonimage.PixelFormat::Rgba8)
let soft = image.blur(1.5)
assert_eq(soft.width, 6)
let crisp = soft.unsharp_mask(radius=1.0, amount=150.0, threshold=3)
assert_eq(crisp.height, 5)
}test {
let image = @moonimage.Image::new(3, 3, @moonimage.PixelFormat::Rgba8)
assert_eq(image.brightness(1.5).contrast(1.2).hue_rotate(90.0).invert().width, 3)
}test {
let under = @moonimage.Image::new(4, 4, @moonimage.PixelFormat::Rgba8)
let over = @moonimage.Image::new(2, 2, @moonimage.PixelFormat::Rgba8)
let drawn = under.composite(over, x=1, y=1, opacity=0.5)
assert_eq(drawn.width, 4)
}test {
let image = @moonimage.Image::new(4, 4, @moonimage.PixelFormat::Rgba8)
let file = @moonimage.encode(image, @moonimage.Format::Gif)
let animation = @moonimage.decode_animation(file)
assert_eq(animation.frames.length(), 1)
assert_eq(animation.compose().length(), 1)
}pub(all) struct Metadata {
gamma : Double?
chromaticities : Chromaticities?
intent : Intent?
icc : Icc?
texts : Array<Text>
}test {
let image = @moonimage.Image::new(3, 2, @moonimage.PixelFormat::Rgb8)
// Six is a quarter turn clockwise, which swaps the canvas.
assert_eq(image.orient(6).width, 2)
assert_eq(image.upright(None).width, 3)
}test {
assert_eq(@moonimage.Limits::default().width, 1_048_576)
assert_eq(@moonimage.Limits::strict().pixels, 67_108_864)
}| Call | What it does |
|---|---|
| decode(bytes, format?, limits?) | read an image, whichever format the bytes are, in the layout asked for or the one the file holds |
| decode_animation(bytes, limits?) | read every frame; a still image is one frame |
| encode(image, format) | write an image, with that format's defaults |
| encode_animation(animation, format) | write the frames, or the first one where the format has no animation |
| Format::of(bytes), Format::extension, Format::mime, Format::is_animated | what the bytes are, and how they are named |
| Image::new, Image::of | an image, blank or over bytes that already exist |
| Image::row, Image::pixel | one row, one pixel |
| Image::convert | the same image in another sample format |
| Image::to_premultiplied, Image::to_straight | colour × alpha, and back |
| Image::flip_horizontal, Image::flip_vertical, Image::rotate, Image::rotate_by, Image::crop | moving pixels without reading them |
| Image::scale | a new size, by any of five filters |
| Image::blur, Image::unsharp_mask | a Gaussian, and the difference that undoes it |
| Image::brightness, Image::contrast, Image::hue_rotate, Image::invert | the four colour knobs |
| Image::composite | another picture laid over this one |
| Image::orient, Image::upright | the way up a camera said the picture was |
| Pixel::to_color, Pixel::of_color, Pixel::rotate_hue | the seam to the colour package |
| Animation::new, Animation::compose | frames, and every frame as a full canvas |
| png.encode, png.decode, png.EncodeOptions, png.Filter | PNG, with its compression level and row filter |
| qoi.encode, qoi.decode | QOI |
| bmp.encode, bmp.decode, bmp.EncodeOptions, bmp.Depth | BMP, 24 or 32 bits |
| tga.encode, tga.decode, tga.EncodeOptions, tga.Depth | TGA, with run lengths |
| jpeg.encode, jpeg.decode, jpeg.EncodeOptions, jpeg.Subsampling, jpeg.exif_orientation | JPEG, and the orientation a camera wrote |
| gif.encode, gif.encode_animation, gif.decode, gif.decode_animation, gif.EncodeOptions | GIF, with dithering and transparency |
| webp.decode, webp.decode_animation, webp.encode, webp.encode_animation | WebP: lossless and lossy read, lossless written |
| tiff.decode, tiff.decode_pages, tiff.encode, tiff.encode_pages, tiff.EncodeOptions, tiff.Compression, tiff.orientation | TIFF, its compressions and its pages |
| ico.decode, ico.decode_all, ico.encode, ico.EncodeOptions, ico.Entry, ico.Kind | icons and cursors, at every size in the file |
| pnm.decode, pnm.encode, pnm.EncodeOptions, pnm.Kind | the portable anymap, four shapes and two spellings |
| hdr.decode, hdr.encode | Radiance HDR, samples above one and all |
| exr.decode, exr.encode, exr.EncodeOptions, exr.Compression, exr.half_to_float, exr.float_to_half | OpenEXR, and the half-precision conversion it turns on |
| farbfeld.decode, farbfeld.encode | farbfeld |
| dds.decode, dds.Block | block-compressed textures, read |
fn decode_animation(bytes : BytesView, format? : PixelFormat, limits? : Limits) -> Animation raise RefusedInstall
Download zipImage codecs for MoonBit: PNG, JPEG, GIF, WebP, BMP, TGA, QOI, TIFF, ICO, PNM, Radiance HDR, OpenEXR, farbfeld and DDS, read and written, with the pixel arithmetic, filters and colour between them.