Class SvgIcon
- All Implemented Interfaces:
Icon
Icon: keeps the SVG source and lazily draws it
(via the installed SvgRenderer) to a cached Image per
requested pixel size (a small LRU of 16 sizes). Because it rasterizes at the exact device size it
is drawn at (see Icon.paint(limn.graphics.Canvas, float, float, float, limn.graphics.Color, boolean)), an SVG icon stays crisp on HiDPI and at
any zoom: no blur from up/down-sampling a fixed bitmap. Draw it tinted to
recolor with the theme (SVG icons are single-color masks, so Icon.tintable()
is true); draw it untinted to keep the SVG's own colors.
Each rasterized Image is cached and its identity is the GPU texture
key, so reusing one SvgIcon reuses a single texture per size across the
whole UI. Drawing needs the backend running (the renderer is installed at startup); call
image() during setup to warm the cache if desired.
An SVG of any aspect is drawn fitted into the box it is given and centred, its longer
side the box's side, as Icon.paint(limn.graphics.Canvas, float, float, float, limn.graphics.Color, boolean) draws any icon.
A source the renderer refuses draws nothing. A malformed SVG, or one with no size of its own, fails at every size, so the first refusal is remembered, logged once as a warning, and answered from then on with an empty bitmap rather than a parse per paint: an exception thrown from a paint is caught by the frame and counted against the window, and enough of them stop the window repainting at all.
An instance is confined to the UI thread: the size cache is a plain
LRU map with no lock, and it is written by every miss. image(int) and
image(int, boolean) therefore reject any other thread once a backend is
running. The one way to move the parse and rasterize off that thread is
imageAsync(int), which does the work on the worker pool and folds the
result back in here.
-
Nested Class Summary
Nested classes/interfaces inherited from interface limn.graphics.Icon
Icon.Mirroring -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe system property that sets the rate every animated SVG plays at, unless its icon says. -
Method Summary
Modifier and TypeMethodDescriptionintintframeMillis(int frame) intstatic SvgIconfromResource(String path) An icon from a classpath resource (e.g.floatheight()image()Base-resolution bitmap, a convenience for warming the cache or ad-hoc draws.image(int pixelSize) Rasterized bitmap fit intopixelSizepx (cached; stable identity, because that identity is the GPU texture key).image(int pixelSize, boolean dark) Iconentry point: rasterizes at the requested device size (theme brightness is irrelevant; SVG icons recolor by tint, not by variant).image(int pixelSize, boolean dark, int frame) One frame of the bitmapIcon.image(int, boolean)answers, which is that method's answer for frame 0.imageAsync(int pixelSize) Rasterizes forpixelSizeon theUiworker pool and folds the bitmap into this icon's cache on the UI thread, so the nextimage(int)at that size is a hit and no frame pays for the parse.static voidinstallRenderer(SvgRenderer newRenderer) Installs the backend's renderer (called by the backend at startup).booleanAn SVG is animated when it has SMIL or CSS animation, which the toolkit reads and plays itself, a moment a frame.static booleanWhether SVG icons can be drawn at all; without a renderer they draw nothing.static SvgIconAn icon from a file.intOne whose animations repeat without end plays without end; one whose all end plays once and rests.static SvgIconAn icon from raw SVG source.static voiduninstallRenderer(SvgRenderer expected) Removes the renderer if it is stillexpected, so a late teardown cannot clear a newer one.floatwidth()withFrameRate(int framesPerSecond) The same SVG, playing atframesPerSecondif it is animated: its own rate, which wins over "limn.svg.frameRate".
-
Field Details
-
FRAME_RATE_PROPERTY
The system property that sets the rate every animated SVG plays at, unless its icon says.- See Also:
-
-
Method Details
-
withFrameRate
The same SVG, playing atframesPerSecondif it is animated: its own rate, which wins over "limn.svg.frameRate". An SVG says nothing of a rate, since neither SMIL nor CSS has one: both measure time, and the toolkit draws that time as frames, 24 a second unless set, the rate of film.- Parameters:
framesPerSecond- from 1 to 120- Returns:
- a new icon of the same source
- Throws:
IllegalArgumentException- when the rate is out of that range
-
frameRate
public int frameRate()- Returns:
- the rate it plays at: its own, else the system property's, else 24
-
isAnimated
public boolean isAnimated()An SVG is animated when it has SMIL or CSS animation, which the toolkit reads and plays itself, a moment a frame. Reading it parses the source on the first call.- Specified by:
isAnimatedin interfaceIcon- Returns:
- whether this icon has more than one frame, which a component plays; false unless overridden
-
frameCount
public int frameCount()- Specified by:
frameCountin interfaceIcon- Returns:
- how many frames; 1 for a still icon
-
frameMillis
public int frameMillis(int frame) - Specified by:
frameMillisin interfaceIcon- Parameters:
frame- from 0 toIcon.frameCount()- 1- Returns:
- how long that frame is shown, in milliseconds; 0 for a still icon
-
loopCount
public int loopCount()One whose animations repeat without end plays without end; one whose all end plays once and rests. -
image
One frame of the bitmapIcon.image(int, boolean)answers, which is that method's answer for frame 0. A frame of an animation decoded as it plays is decoded here, on the calling thread, if it is not in memory. A frame of an animated SVG is drawn at its moment, fitted into the box; drawn once and kept, for a size whose frames together fit in sixteen megabytes. UI thread.- Specified by:
imagein interfaceIcon- Parameters:
pixelSize- target extent in physical pixels (≥ 1)dark- whether the active theme is darkframe- from 0 toIcon.frameCount()- 1- Returns:
- the frame, of the size frame 0 is
-
of
An icon from raw SVG source. -
fromResource
An icon from a classpath resource (e.g."/limn/demo/icons/search.svg").Reads the resource on the calling thread and needs no asynchronous form: an icon is a few kilobytes of text, read once from a jar the class loader already has open, and nothing is parsed or rasterized here; the expensive half happens later, in
image(int), which does have one. -
load
An icon from a file. A picture the file shows by an address relative to it is read from the file's own folder, or from one inside it, and from nowhere else; no address is ever read from the network.Reads the file on the calling thread, as
fromResource(java.lang.String)reads a resource: an SVG is text, and the pictures it shows are read when it is first drawn.- Parameters:
file- the SVG file- Returns:
- the icon
- Throws:
UncheckedIOException- when the file cannot be readMalformedContentException- when it is larger than an SVG is read
-
installRenderer
Installs the backend's renderer (called by the backend at startup). -
uninstallRenderer
Removes the renderer if it is stillexpected, so a late teardown cannot clear a newer one. -
isRendererInstalled
public static boolean isRendererInstalled()Whether SVG icons can be drawn at all; without a renderer they draw nothing. -
image
Rasterized bitmap fit intopixelSizepx (cached; stable identity, because that identity is the GPU texture key).A miss parses and rasterizes the SVG on the calling thread, and the calling thread is normally the UI thread inside
Icon.paint(limn.graphics.Canvas, float, float, float, limn.graphics.Color, boolean), so the first paint at a new content scale, at a new size step, or at each step of a zoom gesture, pays a parse and a rasterize inside that frame.imageAsync(int)is the form that pays it on the worker pool; calling that ahead of the paint (on a scale change, or during setup) is what keeps the frame clean. There is no asynchronous form ofIcon.image(int, boolean)itself, and there should not be: a paint asking for a bitmap has to be answered during that paint.A source the renderer refuses is answered with an empty bitmap, at this size and at every size not already cached, and is not handed to the renderer again.
- Parameters:
pixelSize- target extent in device pixels; values below 1 are treated as 1- Throws:
IllegalStateException- if called off the UI thread while a backend is running, or if noSvgRendereris installed
-
imageAsync
Rasterizes forpixelSizeon theUiworker pool and folds the bitmap into this icon's cache on the UI thread, so the nextimage(int)at that size is a hit and no frame pays for the parse. Returned unstarted:icon.imageAsync(px) .onSuccess(bitmap -> requestRepaint()) .deliverIf(this::isShowing) .start();The delivered
Imageis the one this icon now has cached for that size, so drawing it and drawingimage(int)share a single texture. A size already cached is delivered without rasterizing anything again, but a size still in flight is not: unlikeImages.loadShared(java.nio.file.Path), two calls for one size before the first lands rasterize twice, and the second bitmap replaces the first in the cache. Warm a size once, or hold theJoband cancel it before asking again.Cancelling stops the delivery, not the work: if the rasterize had already finished, the bitmap is still folded into the cache; it is paid for either way, and the next paint at that size may as well have it. A failure (no installed renderer, malformed SVG) reaches
onFailureon the UI thread; nothing is thrown from here. A refused source is remembered asimage(int)remembers it, and asking again fails with the same cause without rasterizing.- Parameters:
pixelSize- target extent in device pixels; values below 1 are treated as 1- Throws:
IllegalStateException- if called off the UI thread, or if no backend is running
-
image
Iconentry point: rasterizes at the requested device size (theme brightness is irrelevant; SVG icons recolor by tint, not by variant). UI thread, with the miss cost described onimage(int). -
width
public float width()- Returns:
- its own width, in its own units, which the toolkit takes as logical points: what an image view showing it measures at when nothing else sets its size. 0 for a source the renderer refuses. Read on the first call, which opens the document on this thread
- Throws:
IllegalStateException- if noSvgRendereris installed
-
height
public float height()- Returns:
- its own height; see
width()
-
image
Base-resolution bitmap, a convenience for warming the cache or ad-hoc draws. UI thread.
-