Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions examples/graphics2d/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Java2D/Graphics2D drawing example

JWM raster example working with the standard `Java2D/Graphics2D` as a graphics-drawing library.

This example shows some text and an orange circle at the center of the main window.

Requires Java 22+ to compile/run, as it uses FFM
([Foreign Function & Memory](https://docs.oracle.com/en/java/javase/25/core/foreign-function-and-memory-api.html))
to access/share the screen pixels' native memory buffers created by JWM.
26 changes: 26 additions & 0 deletions examples/graphics2d/java/EventFrameGraphics2D.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package io.github.humbleui.jwm.examples;

import java.awt.image.BufferedImage;

import io.github.humbleui.jwm.Event;

/// Frame event for Java2D/Graphics2D graphics-drawing library.
///
public class EventFrameGraphics2D implements Event {

private final BufferedImage bufferedImage;

public EventFrameGraphics2D(BufferedImage bufferedImage) {
this.bufferedImage = bufferedImage;
}

public BufferedImage getBufferedImage() {
return bufferedImage;
}

@Override
public String toString() {
return "EventFrameGraphics2D(bufferedImage=" + System.identityHashCode(bufferedImage) + ")";
}

}
67 changes: 67 additions & 0 deletions examples/graphics2d/java/Example.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package io.github.humbleui.jwm.examples;

import io.github.humbleui.jwm.*;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.util.function.Consumer;

/// An example of JWM working with Java2D/Graphics2D as graphics-drawing library.
///
/// This example shows some text and an orange circle at the center of the main window.
///
public class Example implements Consumer<Event> {

private Window window;

public Example(int width, int height) {
window = App.makeWindow();
window.setEventListener(this);
window.setTitle("JWM with Java2D/Graphics2D Fixed Scale");

window.setLayer(new LayerRasterGraphics2D());

window.setWindowSize(width, height);
window.setVisible(true);
}

@Override
public void accept(Event e) {
if (e instanceof EventWindowCloseRequest) {
window.close();
App.terminate();
}
else if (e instanceof EventFrameGraphics2D eg) {
var image = eg.getBufferedImage();
var g2d = image.createGraphics();
drawJava2DScene(g2d, image.getWidth(), image.getHeight());
g2d.dispose();
}
}

private void drawJava2DScene(Graphics2D g2d, int w, int h) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

// Clear background
g2d.setColor(new Color(30, 30, 30));
g2d.fillRect(0, 0, w, h);

// Draw an orange circle
int circleSize = Math.min(w, h) / 2;
int x = (w - circleSize) / 2;
int y = (h - circleSize) / 2;

g2d.setColor(Color.ORANGE);
g2d.fillOval(x, y, circleSize, circleSize);

// Draw some text
g2d.setColor(Color.WHITE);
g2d.drawString("Hello from High-DPI Correlated Java2D!", 50, 50);
}

public static void main(String[] args) {
App.start(() -> new Example(800, 600));
}

}
47 changes: 47 additions & 0 deletions examples/graphics2d/java/LayerRasterGraphics2D.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package io.github.humbleui.jwm.examples;

import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.Raster;
import java.awt.image.SinglePixelPackedSampleModel;

import io.github.humbleui.jwm.LayerRaster;

/// Raster layer implementation for Java2D/Graphics2D graphics-drawing library.
///
public class LayerRasterGraphics2D extends LayerRaster {

private BufferedImage renderBuffer;

@Override
public void frame() {
// re-initialize the raster buffer if needed
initRasterBuffer(getWidth(), getHeight());
_window.accept(new EventFrameGraphics2D(renderBuffer));
swapBuffers();
}

private void initRasterBuffer(int width, int height) {
if (width <= 0 || height <= 0) return;

// If the buffer size hasn't changed, skip re-allocation
if (renderBuffer != null && renderBuffer.getWidth() == width && renderBuffer.getHeight() == height) {
return;
}

// Use custom DataBuffer wrapper around the JWM native pixel buffer
var nativeDataBuffer = new NativeIntDataBuffer(getPixelsPtr(), width * height);

// Construct a Custom WritableRaster linking to native memory buffer
int[] bandMasks = {0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000}; // RGBA masks
var sampleModel = new SinglePixelPackedSampleModel(
DataBuffer.TYPE_INT, width, height, width, bandMasks
);
var raster = Raster.createWritableRaster(sampleModel, nativeDataBuffer, null);

// Build the BufferedImage for rendering
renderBuffer = new BufferedImage(ColorModel.getRGBdefault(), raster, true, null);
}

}
33 changes: 33 additions & 0 deletions examples/graphics2d/java/NativeIntDataBuffer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.github.humbleui.jwm.examples;

import static java.lang.foreign.ValueLayout.JAVA_INT;

import java.awt.image.DataBuffer;
import java.lang.foreign.MemorySegment;

/// Custom DataBuffer wrapping a native memory segment
/// as backing data "array".
///
public class NativeIntDataBuffer extends DataBuffer {

private final MemorySegment segment;

public NativeIntDataBuffer(long dataIntPtr, int size) {
super(DataBuffer.TYPE_INT, size);
this.segment = MemorySegment.ofAddress(dataIntPtr)
.reinterpret(size * JAVA_INT.byteSize());
}

@Override
public int getElem(int bank, int i) {
// Read directly from native memory space
return segment.getAtIndex(JAVA_INT, i);
}

@Override
public void setElem(int bank, int i, int val) {
// Write directly to native memory space
segment.setAtIndex(JAVA_INT, i, val);
}

}
2 changes: 1 addition & 1 deletion script/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def main():
'examples/dashboard/resources'
]
sources = build_utils.files(f'examples/{args.example}/java/**/*.java')
build_utils.javac(sources, f'examples/{args.example}/target/classes', classpath = classpath, release='16')
build_utils.javac(sources, f'examples/{args.example}/target/classes', classpath = classpath, release='22')

# run
subprocess.check_call([
Expand Down
Loading