Skip to content

Traroth/denise4j

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

denise4j

A Java graphics effects library inspired by the demoscene of the 80s and 90s. Produces animated images from pixel sources, tile maps, and chainable effects — with no dependency on any GUI toolkit.


Available effects

Coordinate transforms

Effect Method Description
Horizontal scroll scrollH(ParamInt offset) Shifts the source horizontally
Vertical scroll scrollV(ParamInt offset) Shifts the source vertically
Bilinear zoom zoom(ParamDouble factor) Zoom in/out centred on the Stage
Explicit-centre zoom zoom(ParamDouble factor, ParamDouble cx, ParamDouble cy) Explicit centre
Rotation rotate(ParamDouble angle) Clockwise rotation, Stage centre
Explicit-centre rotation rotate(ParamDouble angle, ParamDouble cx, ParamDouble cy) Explicit centre

Colour transforms

Applied per pixel after sampling, before the transparent-colour test. Multiple transforms chain in declaration order.

Effect Method Description
Palette cycling exactCycling(int[] palette, ParamDouble phase) Rotates palette entries by exact ARGB match; non-palette pixels pass through
Fade to black fade(ParamDouble t) t=0 identity → t=1 full black; values clamped to [0, 1]
Fade to colour fade(ParamDouble t, int targetArgb) Same as above, fades toward an explicit ARGB colour
Negative negative() Inverts R, G, B channels (255 - c); stateless
Greyscale grayscale() BT.601 luma: (77R + 150G + 29B) >> 8; stateless
LUT remap lut(int[] red, int[] green, int[] blue) Per-channel lookup table, 256 entries each; output masked to [0, 255]

Positioned objects

Spritefr.dufrenoy.imagefx.sprite

A sprite combines a PixelSource with a position, a depth, and an optional anchor point. Position and depth are controlled via ParamDouble / ParamInt and can be updated between frames.

ParamDouble x     = new ParamDouble(100);
ParamDouble y     = new ParamDouble(80);
ParamInt    depth = new ParamInt(0);

Sprite ship = new Sprite(new ImageSource(shipImage), x, y, depth);
// centred anchor:
Sprite bullet = new Sprite(new ImageSource(bulletImage), x, y, depth, 0.5, 0.5);

SpriteLayer

A mutable container of sprites rendered back-to-front (painter's algorithm) into a Stage. Lower depth values appear in front; equal depths are drawn in reverse insertion order (first-added in front). A layer-level transparent colour (color key) can be set once and applies to all sprites in the layer.

SpriteLayer layer = new SpriteLayer()
    .transparentColor(0xFF00FF00); // color key: transparent green

layer.addSprite(ship);
layer.addSprite(bullet);

// Register in pipeline at the desired compositing position
EffectPipeline pipeline = new EffectPipeline()
    .addSource(background)
        .scrollH(bgScroll)
    .addSpriteLayer(layer)  // composited on top of the background
    .build();

// Each frame — update params, then render
x.set(newX);
y.set(newY);
pipeline.render(stage);

Sprites can be added or removed between frames; the layer content is mutable after build(). Sprites that extend partially outside the stage are clipped silently.


Quick start

// 1. Pixel sources
TileSet tileSet = new TileSet(spriteSheet, 16, 16);
TileMap background = new TileMap(tileSet, 40, 30, TileMap.EdgePolicy.WRAP);
background.setTiles(0, 0, levelData);

ImageSource overlay = new ImageSource(overlayImage);

// 2. Mutable parameters (updated each frame)
ParamInt bgScroll  = new ParamInt(0);
ParamInt fgScroll  = new ParamInt(0);
ParamDouble fgZoom = new ParamDouble(1.0);

// 3. Pipeline (built once, reused every frame)
EffectPipeline pipeline = new EffectPipeline()
    .addSource(background)
        .scrollH(bgScroll)
    .addSource(overlay)
        .transparentColor(0xFF00FF00)   // color key: transparent green
        .scrollH(fgScroll)
        .zoom(fgZoom)
    .build();

// 4. Stage (output buffer)
Stage stage = new Stage(640, 480);   // black background by default

// 5. Animation loop
while (running) {
    bgScroll.add(1);
    fgScroll.add(3);
    pipeline.render(stage);

    // Display — your choice of toolkit
    Graphics g = canvas.getGraphics();
    g.drawImage(stage.getImage(), 0, 0, null);
}

Architecture

PixelSource          ← interface: any pixel source
├── ImageSource      ← wraps a BufferedImage
└── TileMap          ← tile grid (WRAP / CLIP / FEED)
        └── TileSet  ← spritesheet split into fixed-size tiles

ParamInt             ← mutable discrete parameter (pixel offset, ...)
ParamDouble          ← mutable continuous parameter (zoom factor, angle, ...)

EffectPipeline       ← effect chain — stateless, renders into a Stage
Stage                ← 32-bit ARGB pixel buffer — output to the toolkit of your choice

StagePool            ← pool of N stages for double/triple buffering
Orchestrator         ← render loop on a dedicated thread, paced at targetFps
FrameCallback        ← interface: callback invoked once per frame before rendering

PerformanceSampler   ← optional tumbling-window accumulator for FPS and render-time stats

Principles

  • Portable — no dependency on Swing, JavaFX, or SWT. The output is a BufferedImage, displayable in any toolkit or exportable to a file.
  • Stateless — the pipeline is built once, reused every frame. Only ParamInt/ParamDouble values change between frames.
  • Direct array access — pixels are read and written via the underlying int[] array (DataBufferInt), bypassing getRGB/setRGB.
  • Bilinear by default — zoom and rotation use bilinear interpolation. The integer path (pure scroll) short-circuits interpolation.

Compositing model

For each pixel (x, y) of the Stage:

  1. Initial value: stage.getBackgroundColor() (opaque black by default)
  2. For each layer, in order:
    • Transforms compute the source coordinate (sx, sy)
    • If the source is bounded (CLIP/FEED) and (sx, sy) is out of bounds → no write
    • The source pixel is sampled (bilinear interpolation for fractional coordinates)
    • Colour transforms are applied in declaration order (exactCycling, fade, negative, grayscale, lut)
    • If the result matches the layer's transparentColor → no write
    • Otherwise → the result overwrites the current Stage value

EdgePolicy

Value Out-of-bounds behaviour
WRAP Coordinates wrapped modulo the source dimensions (Math.floorMod)
CLIP IndexOutOfBoundsException — the pipeline never crosses the boundary
FEED Same runtime behaviour as CLIP — signals semantically that tiles are fed dynamically

Examples

Examples are located in fr.dufrenoy.imagefx.examples. Each is a standalone fullscreen application (AWT, no Swing). Press SPACE to quit.

Class Effect Image
FleursDemoExample Continuous rotation + sinusoidal zoom (mandala effect) flowers.jpg
PaysageDemoExample Multi-directional scrolling on a 3:2 Lissajous curve landscape.png
ShadowDemo 5-layer parallax inspired by Shadow of the Beast — gradient sky + moon, clouds, ochre rock spires, slate menhirs generative
PlatformDemo Platformer level — fixed desert backdrop, tile map (2×2 screens), simulated player with gravity and scripted jumps cloudsinthedesert.png + tiles_spritesheet.png
FractalColorDemo 6 colour effects cycling every 5 s (negative, greyscale, fade, solarize LUT, palette cycling) with continuous rotation + zoom fractal_rainbow_swirl.jpg

Running the demos

Build the self-contained demo JAR (includes all resources):

mvn package -P demos -DskipTests

Then run any demo:

java -jar target/denise4j-demos.jar shadow
java -jar target/denise4j-demos.jar paysage
java -jar target/denise4j-demos.jar fleurs
java -jar target/denise4j-demos.jar platform
java -jar target/denise4j-demos.jar fractal

Press SPACE to quit any demo.

Pre-built binaries are published on the Releases page.


Maven dependency

<dependency>
  <groupId>fr.dufrenoy.imagefx</groupId>
  <artifactId>denise4j</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>

Build and tests

# Compile and run tests
mvn clean install

# Run tests only
mvn test

# Formal JML verification (requires OpenJML — Linux/macOS)
mvn verify -P openjml-unix

# Formal JML verification (Windows via WSL)
mvn verify -P openjml-windows

Requirements: Java 11+, Maven 3.8+


Roadmap

  • Positioned objects — iteration 1: Sprite + SpriteLayer (basic positioning, depth sort, clipping, transparent colour)
  • Positioned objects — iterations 2–4: animation, transforms, composite trees, copper/raster bars
  • Deformations (wave, distortion, tunnel, plasma)
  • Advanced compositing (blending, masking, overlay)

Licence

GNU Lesser General Public License v3 — see LICENCE.

About

Chainable pixel effects (scroll, zoom, rotation) for Java, inspired by the demoscene. Toolkit-agnostic — outputs a BufferedImage.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages