Images in SVG
The SVG backend provides support for bitmap and SVG images through <image> elements. Unlike raster-based backends, SVG doesn't load actual pixel data. Instead, it creates image references that the browser loads and renders. Per the documentation, it must support at least JPEG, PNG, and SVG formats.
How SVG Handles Bitmaps
SVG handles bitmaps differently from other backends:
- No pixel manipulation: SVG creates
<image>elements withhrefattributes pointing to image sources. - Browser-handled loading: the browser handles fetching images and respecting CORS policies.
- Vector context: bitmaps are embedded in a vector graphics context and can have SVG transformations and filters applied.
Loading Bitmap References
In the SVG backend, "loading" a bitmap creates an SvgImageRef which stores the image URL and optional dimensions. The specifier type is String (representing a URL or data URI).
// Load from URL
val imageRef = "https://example.com/image.png".loadBitmap[SvgImageRef]
// Load from data URI
val dataUri = "data:image/png;base64,iVBORw0KGgoAAAANS..."
val dataRef = dataUri.loadBitmap[SvgImageRef]
// Load from relative path
val localRef = "images/logo.svg".loadBitmap[SvgImageRef]
Note* that loadBitmap returns an IO[SvgImageRef]. Use .unsafeRunSync() to extract the value in examples, or handle the IO properly in production code.
Specifying Dimensions
You can optionally specify dimensions when creating image references:
// With both width and height
val withDims = SvgLoadBitmap.withDimensions("image.jpg", width = 200, height = 150)
// With width only (height auto-calculated by browser)
val withWidth = SvgLoadBitmap.withWidth("image.png", width = 300)
// With height only (width auto-calculated by browser)
val withHeight = SvgLoadBitmap.withHeight("image.gif", height = 250)
If dimensions are not specified, the SVG backend uses a default size of 100x100 pixels for bounding box calculations.
Converting to Pictures
An SvgImageRef can be converted to a Picture using the toPicture method:
val imageRefIO = "photo.jpg".loadBitmap[SvgImageRef]
val imageRef = imageRefIO.unsafeRunSync()
val picture = imageRef.toPicture
// Or load and convert in one step
val directPictureIO = "photo.jpg".loadBitmap[SvgImageRef].toPicture
val directPicture = directPictureIO.unsafeRunSync()
Composing with Vector Graphics
Since bitmap references become regular Pictures, they can be composed with vector graphics:
val logoIO = for {
imageRef <- "logo.png".loadBitmap[SvgImageRef]
logo = imageRef.toPicture
} yield logo
.on(circle(150).fillColor(Color.lightBlue))
.beside(text("Welcome").fillColor(Color.black))
val composition = logoIO.unsafeRunSync()
SVG Filters on Bitmaps
SVG filters can be applied to bitmap images just like any other Picture:
val filteredIO = for {
imageRef <- "photo.jpg".loadBitmap[SvgImageRef]
image = imageRef.toPicture
} yield image
.blur(3.0)
.dropShadow(4, 4, 2, Color.black.alpha(0.5.normalized))
val filtered = filteredIO.unsafeRunSync()
Complete Example
This example demonstrates loading an image, applying transformations, and composing with vector graphics:
val program = for {
// Load the image reference
imageRef <- "https://example.com/logo.png".loadBitmap[SvgImageRef]
// Convert to Picture
logo = imageRef.toPicture
// Create composition
composition = logo
.scale(0.75, 0.75)
.on(square(200).fillColor(Color.lightGray))
.above(text("Company Name").fillColor(Color.darkGray))
} yield composition
// In production, handle IO properly
// For examples, we can use unsafeRunSync
val result = program.unsafeRunSync()
Important Considerations
CORS Policies
When loading images from external domains, be aware of CORS (Cross-Origin Resource Sharing) policies.
No Pixel Access
Unlike Java2D or Canvas backends, SVG cannot:
- Access individual pixels.
- Convert Pictures to bitmap data.
- Apply pixel-level manipulations.
For these operations, use a raster-based backend.