Package limn.graphics

Interface Icon

All Known Implementing Classes:
BitmapIcon, SvgIcon

public interface Icon
A themeable, resolution-independent glyph, conceptually distinct from an Image, which is just a fixed grid of RGBA pixels for display. An icon knows how to produce the right bitmap for a given device-pixel size and theme brightness, and whether that bitmap is a single-color mask (recolored with the theme) or a finished, pre-colored picture.

This split is why components take an Icon (buttons, labels, tabs, field adornments) while style-free image widgets take a raw Image: an icon adapts to DPI and theme, a picture does not.

Built-in implementations:

An icon may be animated (isAnimated()): a BitmapIcon of an animated Image is. A component plays it by itself, as the image asks, through paint(limn.graphics.Canvas, float, float, float, limn.graphics.Color, boolean): its time is the scene's, it starts at the paint that first shows it, waits where nobody sees it, rests on its last frame once its turns are done, and asks for a frame of its window only when one of its frames is due, repainting the icon's box and nothing more. A component offers no control over it; an icon that must be held, sped or turned otherwise is an image view's to show. A menu shows the first frame, and so does a window's own icon. The system's wish for less motion is not applied: an application that needs an icon to move less ships one that moves less.

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Interface
    Description
    static enum 
    Whether an icon turns around when the interface does.
  • Method Summary

    Modifier and Type
    Method
    Description
    default int
     
    default int
    frameMillis(int frame)
     
    image(int pixelSize, boolean dark)
    The bitmap best suited to a pixelSize×pixelSize box measured in device pixels, for the given theme brightness.
    default Image
    image(int pixelSize, boolean dark, int frame)
    One frame of the bitmap image(int, boolean) answers, which is that method's answer for frame 0.
    default boolean
     
    default int
     
    default void
    paint(Canvas canvas, float x, float y, float size, @Nullable Color tint, boolean dark)
    Paints this icon into a size×size logical box at (x, y): it requests a bitmap at the exact device resolution (size × Canvas.contentScale()) so the result is crisp, then applies tint when tintable (otherwise draws the picture untouched).
    default void
    paint(Canvas canvas, float x, float y, float size, @Nullable Color tint, boolean dark, boolean mirrored)
    Paints this icon, flipped horizontally about its own box when mirrored.
    default boolean
    Whether image(int, boolean) returns a single-color mask to be recolored with the theme (true: SVG, grayscale/mono PNG), or a finished picture to draw as-is (false: pre-colored light/dark variants).
  • Method Details

    • image

      Image image(int pixelSize, boolean dark)
      The bitmap best suited to a pixelSize×pixelSize box measured in device pixels, for the given theme brightness. Vector icons rasterize at exactly this size (so they stay sharp on HiDPI and at any zoom); bitmap icons pick their nearest available variant.

      Answers on the calling thread, and has no asynchronous form on purpose: paint(limn.graphics.Canvas, float, float, float, limn.graphics.Color, boolean) calls it from inside a frame, and a paint that asked for a bitmap has to be given one before it can finish. An implementation whose bitmap is expensive to produce carries the warm-up on its own type instead, where the caller can run it ahead of the frame; SvgIcon.imageAsync(int) is the one that does.

      Parameters:
      pixelSize - target extent in physical pixels (≥ 1)
      dark - whether the active theme is dark (selects light/dark variants)
    • isAnimated

      default boolean isAnimated()
      Returns:
      whether this icon has more than one frame, which a component plays; false unless overridden
    • frameCount

      default int frameCount()
      Returns:
      how many frames; 1 for a still icon
    • frameMillis

      default int frameMillis(int frame)
      Parameters:
      frame - from 0 to frameCount() - 1
      Returns:
      how long that frame is shown, in milliseconds; 0 for a still icon
      Throws:
      IndexOutOfBoundsException - if there is no such frame
    • loopCount

      default int loopCount()
      Returns:
      how many turns the animation makes: 0 for one that does not end, which is also what a still icon answers
    • image

      default Image image(int pixelSize, boolean dark, int frame)
      One frame of the bitmap 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.
      Parameters:
      pixelSize - target extent in physical pixels (≥ 1)
      dark - whether the active theme is dark
      frame - from 0 to frameCount() - 1
      Returns:
      the frame, of the size frame 0 is
      Throws:
      IndexOutOfBoundsException - if there is no such frame
    • tintable

      default boolean tintable()
      Whether image(int, boolean) returns a single-color mask to be recolored with the theme (true: SVG, grayscale/mono PNG), or a finished picture to draw as-is (false: pre-colored light/dark variants). Governs whether paint(limn.graphics.Canvas, float, float, float, limn.graphics.Color, boolean) applies its tint.
    • paint

      default void paint(Canvas canvas, float x, float y, float size, @Nullable Color tint, boolean dark)
      Paints this icon into a size×size logical box at (x, y): it requests a bitmap at the exact device resolution (size × Canvas.contentScale()) so the result is crisp, then applies tint when tintable (otherwise draws the picture untouched). This is the one idiom every component uses to draw an icon.

      An animated icon is drawn at the frame of the moment for the widget painting it, whose clock this call starts; the box is then taken in that widget's own coordinates, as every component paints its icons, since it is the box a due frame repaints. Painted from outside a widget's paint, it shows its first frame.

    • paint

      default void paint(Canvas canvas, float x, float y, float size, @Nullable Color tint, boolean dark, boolean mirrored)
      Paints this icon, flipped horizontally about its own box when mirrored.

      The flip is a negative x scale about the destination rect, and it is the one legal negative scale in this toolkit: it acts on a single image, never on a tree. Mirroring a layout is a placement decision taken by the widget that owns the coordinate, and a transform at any root would turn correctly shaped text into a mirror image, flip every picture and every video frame, and put an inverse transform on the hot path of every hit test.

      A caller passes mirrored as mirroring == Mirroring.IN_RTL && widget.layoutDirection() == RTL: the flag says what the icon means and the axis says which way the interface reads, and neither alone is the answer.

      Parameters:
      mirrored - whether to flip; false is exactly paint(Canvas, float, float, float, Color, boolean)