Package limn.graphics

Class PngEncoder

java.lang.Object
limn.graphics.PngEncoder
All Implemented Interfaces:
ImageEncoder

public final class PngEncoder extends Object implements ImageEncoder
The toolkit's built-in ImageEncoder: PNG, 8-bit RGBA with straight alpha, which is Image's pixel layout unchanged, so no channel is dropped, no value is quantized, and decoding the result yields the bytes that went in.

Pure Java, on java.util.zip. It runs with no backend started, no window and no GL context, which is what lets a test produce a reference image and an asset tool run headless. Images installs it, so ImageFormat.PNG is always encodable.

Deterministic: the same Image produces the same bytes, with no timestamp, no producer string, no adaptive choice that depends on anything but the pixels. Two runs of the same JVM are byte-identical, which is what a test comparing against a checked-in file relies on. The compressed payload is java.util.zip's, so a file produced by one JDK is not promised to match one produced by another; a reference file therefore belongs to a pinned toolchain, and a test that must survive a JDK upgrade compares decoded pixels rather than bytes.

No metadata is written: no text chunks, no colour profile, no timestamp. A PNG this writes carries pixels and nothing else.

  • Field Details

  • Method Details

    • name

      public String name()
      Description copied from interface: ImageEncoder
      A short, stable, lower-case identifier used in diagnostics. It appears in the failure raised when nothing accepts a request, which is the only way anyone finds out which encoders existed and in what order they were consulted.

      It carries no meaning and nothing may branch on it; the wrong edit this prevents is a comparison against a literal name somewhere on a control-flow path, which would put one encoder's identity into logic that is supposed to be encoder-neutral.

      Deliberately without a default: deriving it from the implementing class produces an unreadable synthetic name for a lambda or an anonymous class, in exactly the message that exists to be readable.

      Specified by:
      name in interface ImageEncoder
      Returns:
      the identifier, never null and never blank
    • supports

      public boolean supports(ImageEncodeOptions options)
      Description copied from interface: ImageEncoder
      Whether this encoder will take options: the whole request, not just the format, so an encoder that supports a format only over part of the quality range can decline the rest instead of silently rounding it.

      Cheap and honest, and it must never throw: it runs on the caller's thread, once per installed encoder, every time anything is encoded, and one encoder that throws here makes the probe unusable for every encoder behind it.

      Declaring support is the whole reason this method exists. An encoder that claims a format it cannot fully represent (alpha into a format without an alpha channel, say) must decline rather than accept and drop the channel: a caller who is refused can choose another format, whereas a caller handed a degraded file finds out when someone looks at it.

      Deliberately without a default, because both possible defaults are wrong: claiming everything destroys the ordering, and claiming nothing makes an encoder that forgot to override it silently unreachable.

      Specified by:
      supports in interface ImageEncoder
    • encode

      public void encode(Image image, ImageEncodeOptions options, OutputStream out) throws IOException
      Description copied from interface: ImageEncoder
      Writes image to out in the requested format. Called only after ImageEncoder.supports(limn.graphics.ImageEncodeOptions) returned true for the same options. Does not close out and does not flush it; the caller owns the stream.

      Orientation is not this method's business: image is top-down (row 0 at the top) by Image's contract and every format this writes is defined against that same order. The wrong edit this prevents is "correcting" the row order here to match a GL framebuffer's bottom-up layout; the flip belongs to whatever read those pixels back (see ReadableSurface), and a second flip here would cancel it for one path and not the other, producing an upside-down file with no other symptom.

      Alpha is straight, never premultiplied, likewise by Image's contract.

      The same image and the same options must produce the same bytes. Determinism is what makes an exported file comparable against a checked-in reference; an encoder that stamps a timestamp, a producer string or a random seed into its output breaks every such test.

      Specified by:
      encode in interface ImageEncoder
      Throws:
      IOException - if out fails