Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Images

Guido supports displaying both raster images (PNG, JPEG, GIF, WebP) and SVG vector graphics. Images are rendered as GPU textures and compose seamlessly with container transforms.

Basic Usage

The image() function creates an image widget from a file path:

#![allow(unused)]
fn main() {
use guido::prelude::*;

// Load a PNG image
image("./icon.png")
    .width(32.0)
    .height(32.0)

// Load an SVG (auto-detected by extension)
image("./logo.svg")
    .width(100.0)
    .height(100.0)
}

Image Sources

You can load images from different sources using ImageSource:

#![allow(unused)]
fn main() {
// From file path (raster)
ImageSource::Path("./photo.jpg".into())

// From memory (raster)
ImageSource::Bytes(image_bytes.into())

// From file path (SVG)
ImageSource::SvgPath("./icon.svg".into())

// From memory (SVG)
ImageSource::SvgBytes(svg_string.as_bytes().into())
}

When using a string path with image(), the file extension determines the type automatically: .svg files use SVG rendering, all others use raster decoding.

Sizing

You can specify explicit dimensions or let the image use its intrinsic size:

#![allow(unused)]
fn main() {
// Explicit width and height
image("./icon.png")
    .width(32.0)
    .height(32.0)

// Only width - height calculated from aspect ratio
image("./banner.png")
    .width(200.0)

// Only height - width calculated from aspect ratio
image("./logo.svg")
    .height(48.0)

// No size - uses intrinsic dimensions
image("./icon.png")
}

Content Fit Modes

The content_fit() method controls how images fit within their bounds:

ModeDescription
ContentFit::ContainFit within bounds, preserving aspect ratio (default)
ContentFit::CoverCover the bounds, may crop, preserving aspect ratio
ContentFit::FillStretch to fill exactly, ignoring aspect ratio
ContentFit::NoneUse intrinsic size, ignoring widget bounds
#![allow(unused)]
fn main() {
// Cover mode - fills the space, may crop
image("./photo.jpg")
    .width(200.0)
    .height(150.0)
    .content_fit(ContentFit::Cover)
}

Transform Composition

Images inherit transforms from parent containers, just like text:

#![allow(unused)]
fn main() {
// Rotated image
container()
    .rotate(15.0)
    .child(
        image("./badge.svg")
            .width(32.0)
            .height(32.0)
    )

// Scaled image
container()
    .scale(1.5)
    .child(
        image("./icon.png")
            .width(24.0)
            .height(24.0)
    )

// Combined transforms
container()
    .rotate(45.0)
    .scale(2.0)
    .child(image("./logo.svg"))
}

SVG Quality

SVGs are automatically rasterized at the appropriate scale for crisp rendering:

  • HiDPI displays: SVGs render at the display scale factor
  • Transforms: When scaled via container transforms, SVGs re-rasterize at the higher resolution
  • Quality: A 2x supersampling multiplier ensures smooth edges

This means SVGs stay crisp regardless of how they’re scaled or transformed.

In-Memory SVGs

For dynamically generated or embedded SVGs:

#![allow(unused)]
fn main() {
let svg_data = r##"
    <svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
        <circle cx="50" cy="50" r="40" fill="#4f46e5" />
    </svg>
"##;

image(ImageSource::SvgBytes(svg_data.as_bytes().into()))
    .width(48.0)
    .height(48.0)
}

Reactive Images

Image sources can be reactive, allowing dynamic image changes:

#![allow(unused)]
fn main() {
let icon_source = create_signal(ImageSource::Path("./play.png".into()));

// The image updates when the signal changes
image(icon_source)
    .width(32.0)
    .height(32.0)

// Change the image on click
container()
    .on_click(move || {
        icon_source.set(ImageSource::Path("./pause.png".into()));
    })
    .child(image(icon_source))
}

Supported Formats

Raster Formats

  • PNG
  • JPEG
  • GIF
  • WebP

Vector Formats

  • SVG

Example

Here’s a complete example showing various image features:

use guido::prelude::*;

fn main() {
    App::new().run(|app| {
        let svg_icon = r##"
            <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
                <circle cx="12" cy="12" r="10" fill="#3b82f6"/>
            </svg>
        "##;

        let view = container()
            .padding(16.0)
            .layout(Flex::row().spacing(16.0))
            .child(
                // PNG image
                image("./logo.png")
                    .width(48.0)
                    .height(48.0)
            )
            .child(
                // SVG from memory
                image(ImageSource::SvgBytes(svg_icon.as_bytes().into()))
                    .width(32.0)
                    .height(32.0)
            )
            .child(
                // Rotated image
                container()
                    .rotate(15.0)
                    .child(
                        image("./icon.svg")
                            .width(24.0)
                            .height(24.0)
                    )
            );

        app.add_surface(
            SurfaceConfig::new()
                .height(80)
                .background_color(Color::rgb(0.1, 0.1, 0.15)),
            move || view,
        );
    });
}

Performance Notes

  • Images are cached as GPU textures
  • The cache holds up to 64 textures with LRU eviction
  • SVGs are re-rasterized when their display scale changes significantly
  • Texture uploads happen once per unique image/scale combination