Package limn.testing

Class NoopCanvas

java.lang.Object
limn.testing.NoopCanvas
All Implemented Interfaces:
Canvas

public class NoopCanvas extends Object implements Canvas
A fixed-size canvas that draws nothing, so a full frame can run with no GL. Only the save/restore depth is real: the toolkit balances it and complains when it does not, and a widget that clipped without restoring would otherwise leak into whatever painted after it.

Not final: a test that needs to see one call overrides that one. It used to exist four times under three names, one copy per module, each saying so.

  • Constructor Details

    • NoopCanvas

      public NoopCanvas(float width, float height)
      Parameters:
      width - what width() answers
      height - what height() answers
  • Method Details

    • width

      public float width()
      Specified by:
      width in interface Canvas
      Returns:
      frame width in logical points
    • height

      public float height()
      Specified by:
      height in interface Canvas
      Returns:
      frame height in logical points
    • contentScale

      public float contentScale()
      Specified by:
      contentScale in interface Canvas
      Returns:
      physical pixels per logical point for this frame
    • clear

      public void clear(Color color)
      Description copied from interface: Canvas
      Clears the whole framebuffer (ignores transform/clip/opacity).
      Specified by:
      clear in interface Canvas
    • save

      public void save()
      Description copied from interface: Canvas
      Pushes a copy of the current state (transform, opacity, clip, blend mode, sampling, pixel snap).
      Specified by:
      save in interface Canvas
    • restore

      public void restore()
      Description copied from interface: Canvas
      Pops back to the previously saved state.
      Specified by:
      restore in interface Canvas
    • saveCount

      public int saveCount()
      Description copied from interface: Canvas
      How many Canvas.save()s are outstanding, the depth of the state stack.

      It exists so that a caller can put the canvas back where it found it without knowing how many saves happened in between, which is the only way to be safe against code it does not control: a widget's paint may push clips, may push more of them down a branch, and may stop halfway by throwing. Widget.paintWidget reads this before painting a widget and trims back to it afterwards, so one widget's leak cannot become every ancestor's.

      Specified by:
      saveCount in interface Canvas
      Returns:
      the number of outstanding saves; 0 on a canvas nobody has saved on
    • restoreToCount

      public void restoreToCount(int count)
      Description copied from interface: Canvas
      Pops saves until Canvas.saveCount() equals count.

      Does nothing when the depth is already at or below count: it is a trim, not an assertion, and a caller using it to recover from someone else's mistake must not be given a second mistake to handle. Never pops past zero.

      Specified by:
      restoreToCount in interface Canvas
      Parameters:
      count - the depth to return to, normally one taken from Canvas.saveCount() earlier
    • translate

      public void translate(float dx, float dy)
      Specified by:
      translate in interface Canvas
    • scale

      public void scale(float sx, float sy)
      Specified by:
      scale in interface Canvas
    • rotate

      public void rotate(float angleRadians)
      Description copied from interface: Canvas
      Rotates by angleRadians (positive = clockwise, y grows down).
      Specified by:
      rotate in interface Canvas
    • setOpacity

      public void setOpacity(float opacity)
      Description copied from interface: Canvas
      Multiplies the current state's opacity by opacity, clamped to [0..1]; every subsequent paint's alpha is multiplied by the result. Restored by Canvas.restore().

      It multiplies rather than replaces so that an opacity set inside a fading surface, or inside a layer, combines with the one around it instead of undoing it: a widget at one half inside a panel fading at one half draws at a quarter. On a fresh Canvas.save() the current opacity is whatever the enclosing state carries, which is 1 at the top of a frame and inside a layer, so a caller that saves and sets once gets the number it asked for. A caller that wants an absolute value has to know what it is multiplying.

      This is a per-draw alpha: each draw is translucent on its own, so where one draw covers another the one beneath shows through it. To fade overlapping draws as one picture, open a layer instead.

      Specified by:
      setOpacity in interface Canvas
      Parameters:
      opacity - the factor, clamped to [0..1]
    • opacity

      public float opacity()
      Specified by:
      opacity in interface Canvas
      Returns:
      the current state's opacity, the product of every Canvas.setOpacity(float) still in force
    • clipRect

      public void clipRect(float x, float y, float w, float h)
      Description copied from interface: Canvas
      Intersects the clip with an axis-aligned rectangle (in current coordinates). V1 limitation: under a rotated transform the clip degrades to the device-space AABB of the transformed corners, a superset of the requested region (content may show in the AABB's corners). Axis-aligned transforms clip exactly.
      Specified by:
      clipRect in interface Canvas
    • clipRoundRect

      public void clipRoundRect(RoundRect roundRect)
      Description copied from interface: Canvas
      Intersects the clip with a rounded rectangle. The corners are this rectangle's own: a Canvas.clipRect(float, float, float, float) made before or after it cuts the region straight and never rounds where it cuts, so a repaint confined to part of a rounded surface draws that part exactly as a whole frame does.

      V1 limitations: the clip state tracks one rounded rect, so nested rounded clips intersect their rectangles exactly and the region they leave is rounded by the most recent corner radius alone, as a single uniform radius (the max of the four corners, clamped to that region's half extents). That is exact when the inner rounded rect lies inside the outer one; where the two only overlap, every corner of the region they leave is rounded by the inner radius, whichever rectangle it came from. A rounded rect whose corners are all square clips like clipRect and leaves an earlier one's corners as they were. Rotation degrades to the AABB like clipRect.

      Specified by:
      clipRoundRect in interface Canvas
    • fillRect

      public void fillRect(float x, float y, float w, float h, Paint paint)
      Specified by:
      fillRect in interface Canvas
    • drawRect

      public void drawRect(float x, float y, float w, float h, float strokeWidth, Paint paint)
      Description copied from interface: Canvas
      Strokes the rectangle outline, centered on its boundary.
      Specified by:
      drawRect in interface Canvas
    • fillRoundRect

      public void fillRoundRect(RoundRect roundRect, Paint paint)
      Specified by:
      fillRoundRect in interface Canvas
    • drawRoundRect

      public void drawRoundRect(RoundRect roundRect, float strokeWidth, Paint paint)
      Description copied from interface: Canvas
      Strokes the rounded-rect outline, centered on its boundary.
      Specified by:
      drawRoundRect in interface Canvas
    • fillCircle

      public void fillCircle(float cx, float cy, float radius, Paint paint)
      Specified by:
      fillCircle in interface Canvas
    • drawCircle

      public void drawCircle(float cx, float cy, float radius, float strokeWidth, Paint paint)
      Specified by:
      drawCircle in interface Canvas
    • fillEllipse

      public void fillEllipse(float cx, float cy, float radiusX, float radiusY, Paint paint)
      Specified by:
      fillEllipse in interface Canvas
    • drawEllipse

      public void drawEllipse(float cx, float cy, float radiusX, float radiusY, float strokeWidth, Paint paint)
      Specified by:
      drawEllipse in interface Canvas
    • drawLine

      public void drawLine(float x1, float y1, float x2, float y2, float strokeWidth, Paint paint)
      Description copied from interface: Canvas
      Line segment with round caps. Axis-aligned lines are pixel-snapped.
      Specified by:
      drawLine in interface Canvas
    • fillPath

      public void fillPath(Path2D path, Paint paint)
      Description copied from interface: Canvas
      Fills a path. Each closed subpath is filled independently (see Path2D for the v1 winding limitations).
      Specified by:
      fillPath in interface Canvas
    • drawPath

      public void drawPath(Path2D path, float strokeWidth, Paint paint)
      Description copied from interface: Canvas
      Strokes a path with round caps and joins. V1 limitation: joins are overlapping round caps, so translucent paints double-blend slightly at each vertex.
      Specified by:
      drawPath in interface Canvas
    • drawText

      public void drawText(String text, float x, float y, Font font, Paint paint)
      Description copied from interface: Canvas
      Draws a single line of text with (x, y) at the baseline origin. Glyphs are rasterized at font.size() × the effective device scale (content scale × canvas transform) and cached in a glyph atlas keyed by physical pixel size: bitmaps are never scaled, so text stays sharp at 1.0/1.25/1.5/2.0. Under axis-aligned uniform transforms, glyphs are snapped to the physical pixel grid.

      Full code-point support, surrogate pairs included. A code point the font lacks is resolved against the registered fallback faces, so mixed-script text draws; a code point no face has renders as .notdef.

      This is the shaped path with the shaping done for you. A canvas that has a shaper hands the string to the installed TextRuler and draws the ShapedText it gets back, so contextual forms, ligatures, mark attachment and bidirectional ordering are what any caption gets, not only the widgets that hold a value of their own. What that costs is one memo lookup per call on the ruler's side; what it buys is that the string measured by Canvas.measureText(java.lang.String, limn.graphics.Font) and the string drawn here are the same arithmetic. A caller that draws the same text every frame should still hold a ShapedText and use the overload below — that is the form that pays nothing at all, and the only one that carries a base direction: this one resolves the paragraph from the string alone, left to right where no character is strong, which is wrong exactly for the all-neutral captions a widget could have decided (Widget.shapeText).

      Control characters, \n included, are skipped: multi-line layout belongs to the widget layer. Under anisotropic scale glyphs rasterize at the larger axis and filter on the smaller one.

      Specified by:
      drawText in interface Canvas
    • measureText

      public TextMetrics measureText(String text, Font font)
      Description copied from interface: Canvas
      Measures a single line of text in logical points (baseline-relative, unquantized and therefore independent of the monitor scale). Same code-point rules as Canvas.drawText(java.lang.String, float, float, limn.graphics.Font, limn.graphics.Paint), and the same shaping: a canvas that shapes when it draws measures what it will draw, so a caption is never laid out to one width and painted at another.
      Specified by:
      measureText in interface Canvas
    • drawImage

      public void drawImage(Image image, float x, float y, float w, float h)
      Description copied from interface: Canvas
      Draws image into the rectangle (x, y, w, h) in logical points, scaled with linear filtering, modulated by the current Canvas.opacity(). The GPU texture is created lazily on first use and cached per window, within Images.textureBudget() as Images.setTextureBudget(long) describes: a texture dropped at a frame boundary is made again when its image is next drawn, and none that the frame just ended drew is dropped.
      Specified by:
      drawImage in interface Canvas
    • drawImage

      public void drawImage(Image image, float x, float y, float w, float h, Color tint)
      Description copied from interface: Canvas
      Draws image tinted by tint (its RGB and alpha are multiplied by the tint's), the path for modulating a picture (e.g. ImageView.setTint). A white opaque tint draws it unchanged. A black source stays black: to recolor a mask, use Canvas.drawImageMask(limn.graphics.Image, float, float, float, float, limn.graphics.Color).
      Specified by:
      drawImage in interface Canvas
    • drawSurface

      public void drawSurface(GpuSurface surface, float x, float y, float w, float h)
      Nothing to composite into: a measuring canvas draws nothing.
      Specified by:
      drawSurface in interface Canvas
    • drawImage

      public void drawImage(Image image, float srcX, float srcY, float srcW, float srcH, float dstX, float dstY, float dstW, float dstH)
      Description copied from interface: Canvas
      Draws the source rectangle (srcX, srcY, srcW, srcH), in image pixels, into the destination rectangle (dstX, dstY, dstW, dstH) in logical points. This is the sprite primitive: frames of a sprite sheet, tiles of a tileset and regions of a texture atlas all draw from one shared Image (one GPU texture, so batching stays unbroken across sprites of the same sheet).

      The source rectangle is not clamped: coordinates outside the image sample the edge texels (CLAMP_TO_EDGE). With Sampling.SMOOTH, half a texel of bleed from neighboring atlas cells is possible at the edges; pack sheets with 1px gutters or use Sampling.PIXELATED for exact cells. Gutters only protect mip level 0: SMOOTH minification (drawing a cell well below its pixel size) selects coarser mips where whole cells average together; for atlases that will be minified, use PIXELATED or pad cells generously.

      Specified by:
      drawImage in interface Canvas
    • drawImage

      public void drawImage(Image image, float srcX, float srcY, float srcW, float srcH, float dstX, float dstY, float dstW, float dstH, Color tint)
      Description copied from interface: Canvas
      The sprite primitive and the tint, together: one cell of a sheet, modulated by tint exactly as Canvas.drawImage(Image, float, float, float, float, Color) modulates a whole image.

      Both halves already existed and could not be used at once, which left an ordinary thing (a tinted sprite from a sheet) reachable only by splitting the sheet into one Image per cell and losing the batching that is the point of a sheet. Everything the source-rectangle overload says about clamping, bleed and mips applies here unchanged.

      Specified by:
      drawImage in interface Canvas