Reference · Media

image.convert

signature
image.convert(str, str, map | null=) -> map | error(str)

What it does

Reads an image (JPEG, PNG, WebP or GIF), crops it, shrinks it and writes it as another file: { crop: { x, y, w, h }, width, format, quality }, giving { width, height } of what was written. crop is in the image's pixels; width shrinks to that width, keeping the shape, and never enlarges; format is jpeg (the default), png or webp (lossless); quality is the JPEG's, 82 unless given. The file is written whole or not at all, folders on the way are made, and both paths stay inside the program's folder. It does not write over the program's own files, as write_file does not: its .qwe and .qwer files, .env and the databases it has open (since 0.7), whatever the case of the name or a Windows form of it (.ENV, main.qwe.). An image of more than 40 megapixels, or one whose conversion would take more than about 640 MiB, is an error value (since 0.7). At most four conversions run at once, and those running share about 1 GiB; past 64 waiting, it gives an error value at once. It needs the fs effect.

Examples

in a program
image.convert("photos/a.jpg", "cache/a-800.jpg", {    crop: { x: 100, y: 0, w: 1200, h: 675 },   // in the image's pixels    width: 800,                                 // shrunk to it, never enlarged    format: "jpeg", quality: 82,                // jpeg, png or webp (lossless)})image.convert(path, "thumbs/42.webp", { width: 160, format: "webp" })

A thumbnail of an upload: the form, then the route that saves the file, checks with image.info that it is an image at all, and writes a 400-pixel WebP beside it:

in a program
route GET /photos =    <form method="post" action="/photos" enctype="multipart/form-data">        <input type="file" name="photo" accept="image/*" required/>        <button>Upload</button>    </form>route POST /photos {    let f = request.files.photo ?? reply 400 "Choose a photo"    let id = uuid()    let original = save_upload(f, `uploads/${id}`)?    if is_err(image.info(original)) { delete_file(original); reply 415 "Not a JPEG, PNG, WebP or GIF" }    image.convert(original, `thumbs/${id}.webp`, { width: 400, format: "webp" })?    redirect(`/thumbs/${id}.webp`)}route GET /thumbs/:name = files("thumbs", name)

A source that cannot be decoded is an error value, and nothing is written:

print(image.convert("nothing.jpg", "out.jpg", { width: 100 }))print(image.convert("main.qwe", "out.jpg"))
output
err("image: No such file or directory (os error 2)")err("image: not a JPEG, PNG, WebP or GIF image")

Edge cases

write_file("tiny.gif", "GIF89a\u0001\u0000\u0001\u0000\u0000\u0000\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0002D\u0001\u0000;")print(image.info("tiny.gif"))print(image.convert("tiny.gif", "out/tiny.png", { format: "png" }))print(image.info("out/tiny.png"))delete_file("tiny.gif")
output
{ width: 1, height: 1, format: "gif", alpha: true }err("image: Format error decoding Gif: no color table available for current frame")err("image: No such file or directory (os error 2)")
print(image.convert("photo.png", "main.qwe", { format: "png" }))print(image.convert("photo.png", ".env", { format: "png" }))
output
err("image: will not write main.qwe: it is one of the program's own files")err("image: will not write .env: it is the program's settings (.env)")

Errors

A source that is missing or cannot be decoded, a crop outside the image, a target that is one of the program's own files, an image too large to convert, and too many conversions waiting are error values. The options are checked at once as runtime errors: a width outside 1 to 12,000, a format other than jpeg, png or webp, a crop without whole pixels or with w or h of 0, and a path outside the program's folder.

let r = image.convert("nothing.jpg", "out.jpg")print(is_err(r), r.error)
output
true image: No such file or directory (os error 2)
this one fails
print(image.convert("a.png", "b.png", { format: "gif" }))
output
line 1: `image.convert`: format is jpeg, png or webp, got "gif"
this one fails
print(image.convert("a.png", "b.png", { crop: { x: 0, y: 0, w: 0, h: 1 } }))
output
line 1: `image.convert`: crop is { x, y, w, h }, whole pixels, w and h above 0

Chaining

The source path comes first, so a pipeline passes it; ? in a route turns a failed conversion into the request's answer, and the { width, height } can be stored with the file.

in a program
route POST /photos { files: { photo } } {    let src = save_upload(photo, "uploads")    let size = image.convert(src, `web/${uuid()}.jpg`, { width: 1600 })?    db.exec("insert into photos (path, w, h) values (?, ?, ?)", src, size.width, size.height)    redirect("/photos")}
print("nothing.jpg" | image.convert("out.jpg") | is_err)
output
true

See also

image.info, save_upload, file, is_err, uuid

Thread
6Useful? Like it, or add to its thread below.

Thread 0 posts

On image.convert(): questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.

    Edit this page on GitHub · every example's output was made by running it (tools/refcheck.mjs) · questions on Discord or r/quillo