-
Notifications
You must be signed in to change notification settings - Fork 1
JNode Graphics2D
Graphics2D implementation with hardware acceleration support in JNode's AWT peer layer.
JNode provides two Graphics2D implementations for AWT component rendering:
-
Current:
JNodeSurfaceGraphics2D— extendsSurfaceGraphics2D, delegates drawing operations to aSurfaceabstraction -
Deprecated:
JNodeGraphics2D— extendsAbstractSurfaceGraphics2D(which extends GNU Classpath'sAbstractGraphics2D), uses an internalBufferedImagefor software rendering
Both implementations bridge the standard Java 2D API to JNode's video driver layer via the Surface interface.
java.awt.Graphics2D
├── SurfaceGraphics2D (abstract, gui/src/awt/org/jnode/awt/util/)
│ └── JNodeSurfaceGraphics2D (gui/src/awt/org/jnode/awt/)
└── gnu.java.awt.java2d.AbstractGraphics2D (GNU Classpath)
└── AbstractSurfaceGraphics2D (abstract, deprecated, gui/src/awt/org/jnode/awt/util/)
└── JNodeGraphics2D (deprecated, gui/src/awt/org/jnode/awt/)
JNodeSurfaceGraphics2D wraps an AbstractSurface (from the video driver layer) and delegates all rendering through it:
-
getDestinationRaster()lazily creates aBufferedImage(TYPE_INT_ARGB) sized to the component -
Drawing operations (
fill,draw,drawString) delegate tosurface.draw(...),surface.fill(...), orFontManager -
updateRaster()blits theBufferedImageback to the surface viadrawImage(image, 0, 0, null)
This is a double-buffering approach: rendering writes to an offscreen BufferedImage, and updateRaster flushes it to the physical surface.
JNodeGraphics2D uses the older AbstractSurfaceGraphics2D base:
-
getDestinationRaster()lazily creates aBufferedImage(TYPE_INT_ARGB) -
Drawing operations (
rawDrawLine,rawFillRect,rawDrawString) directly call surface methods -
updateRaster()callsrawUpdateRaster()to flush the raster to the surface
AbstractSurfaceGraphics2D also manages rendering modes (PAINT_MODE vs XOR_MODE) via Surface.PAINT_MODE/Surface.XOR_MODE.
The Surface interface (org.jnode.driver.video.Surface) is the core abstraction for paintable 2D regions:
- FrameBuffer-backed: Physical video memory or memory-mapped framebuffer
- BufferedImage-backed: In-memory image buffer used as rendering target
Key surface operations used by Graphics2D:
-
surface.drawCompatibleRaster(...)— blit a raster to the surface -
surface.fillRect(...)/surface.drawLine(...)— primitive drawing -
surface.copyArea(...)— fast copy forcopyArea -
surface.draw(...)— shape rendering viaBasicSurfaceGraphics -
surface.getColorModel()— get theColorModelfor raster compatibility
Both Graphics2D implementations route text rendering through JNodeToolkit.getFontManager():
tk.getFontManager().drawText(surface, getClip(), getTransform(), text, font, x, y, getColor());The FontManager (org.jnode.awt.font.FontManager) dispatches to TrueType (TTFFontData) or BDF (BDFFontContainer) renderers. See Font-Rendering for details.
AbstractSurfaceGraphics2D maintains a mode field:
-
Surface.PAINT_MODE— normal opaque paint (set viasetPaintMode()) -
Surface.XOR_MODE— XOR composite mode for fast drawing (set viasetXORMode(Color))
Only JNodeSurfaceGraphics2D supports full Graphics2D state (AffineTransform, Composite, Stroke, Paint, RenderingHints).
JNodeToolkit.getColorModel() provides the ColorModel for the current display configuration. Graphics2D classes use this to:
- Validate raster compatibility (
isCompatibleRaster) - Convert between pixel formats when blitting images
The video driver layer supports multiple bit-depths (8bpp indexed, 16bpp, 24bpp, 32bpp), and the color model adapts accordingly.
| Class | Role |
|---|---|
BasicSurfaceGraphics |
Simple Graphics implementation delegating to Surface, handles origin translation, clipping, color, font |
SurfaceGraphics2D |
Full Graphics2D state (transform, composite, stroke, paint, hints) delegating simple operations to BasicSurfaceGraphics
|
AbstractSurface |
Base for surface implementations in org.jnode.driver.video.util
|
BufferedImageSurface |
In-memory surface wrapping BufferedImage
|
GraphicsFactory |
Factory for creating appropriate Graphics2D instances per component type |
When a JInternalFrame is occluded, Swing still repaints its full bounds — JNode has no per-component damage tracking. Without clipping, the lower frame's pixels are painted over the frames stacked above it, which is what users see as windows "bleeding" through each other.
JNode fixes this with a list of visible regions in desktop coordinates, computed per window from the JDesktopPane z-order and intersected with each image blit. null means no restriction (the fast path); an empty list means fully covered — draw nothing.
gui/src/awt/org/jnode/awt/swingpeers/SwingToolkit.java:437-487:
List<Rectangle> getWindowPaintRegions(JInternalFrame source) {
if (source == null || source instanceof SwingWindow) return null; // :438-440
final JDesktopPane desktop = source.getDesktopPane();
if (desktop == null) return null; // :441-444
final int sourceZ = desktop.getComponentZOrder(source);
if (sourceZ < 0) return null; // :445-448
final List<Rectangle> occlusions = new ArrayList<Rectangle>();
for (JInternalFrame frame : desktop.getAllFrames()) { // :450-459
if (frame == source || !frame.isVisible() || frame.isIcon()) continue;
final int frameZ = desktop.getComponentZOrder(frame);
if (frameZ >= 0 && frameZ < sourceZ) occlusions.add(frame.getBounds());
}
...
}Swing's z-order is index-based with lower index on top, so frameZ < sourceZ is the "in front of me" test. Iconified and invisible frames are excluded. The source bounds are then clipped to the desktop pane (:460-466); an empty intersection returns an empty list. With no occluders and no desktop clipping the method returns null (:467-476).
private static void subtract(Rectangle region, Rectangle occlusion, List<Rectangle> result) (:489-512) does the region algebra: non-intersecting regions pass through unchanged; an intersecting region is decomposed into at most four rectangles (top band, bottom band, left band, right band) from left/right/top/bottom intersection bounds. Applied iteratively at :477-486:
List<Rectangle> regions = new ArrayList<Rectangle>();
regions.add(sourceBounds);
for (Rectangle occlusion : occlusions) {
final List<Rectangle> remaining = new ArrayList<Rectangle>();
for (Rectangle region : regions) { subtract(region, occlusion, remaining); }
regions = remaining;
}
return regions;The parameter type is JInternalFrame, not SwingBaseWindow — SwingBaseWindow<awtT, …> extends JInternalFrame (SwingBaseWindow.java:52-53), so the narrower type is legal and lets tests pass a plain JInternalFrame with no display.
gui/src/awt/org/jnode/awt/swingpeers/SwingComponentPeer.java:
- New
private SwingBaseWindow getWindowAncestor()(:156-162) walkspeerComponent.getParent()until it hits aSwingBaseWindow. -
getGraphics()(:164-207) applies the clip after the existing translate/clipRect:g.translate(x, y); g.clipRect(0, 0, width, height); if (g instanceof SurfaceGraphics2D) { final SwingBaseWindow window = getWindowAncestor(); if (window != null) { ((SurfaceGraphics2D) g).setPaintClip(toolkit.getWindowPaintRegions(window)); } }
- The
instanceof SurfaceGraphics2Dguard matters.getGraphics()builds the graphics as (:182-183):Graphics g = SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") == null ? new JNodeGraphics2D(this) : GraphicsFactory.getInstance().createGraphics(this);
JNodeGraphics2D extends AbstractSurfaceGraphics2D extends AbstractGraphics2D— not aSurfaceGraphics2D, so the clip is silently dropped on that branch. But JNode setsres.put("gnu.javax.swing.noGraphics2D", "true")atcore/src/core/org/jnode/vm/VmSystem.java:339, so on JNode the factory always returns aJNodeSurfaceGraphics2Dand the clip is live in production. -
DesktopFramePeer.getGraphics()(DesktopFramePeer.java:320-323) is not given a paint clip.
gui/src/awt/org/jnode/awt/util/BasicSurfaceGraphics.java:
| Member | Line | Role |
|---|---|---|
private List<Rectangle> paintClip |
:59 |
the region list; null = unrestricted |
copy constructor BasicSurfaceGraphics(BasicSurfaceGraphics g)
|
:65-75 |
deep-copies the regions so Graphics.create() preserves the clip |
public void setPaintClip(List<Rectangle> regions) |
:77-88 |
null → unrestricted; empty list → paint nothing; otherwise defensive copy dropping null/zero-area regions |
private List<Rectangle> clipToPaint(Rectangle bounds) |
:90-103 |
paintClip == null → [bounds]; else region.intersection(bounds) for each non-empty result |
SurfaceGraphics2D.setPaintClip(List<Rectangle>) (:990-992) just forwards. It is applied in exactly two places — drawImage(Image, int, int, Color, ImageObserver) at :513-535 and drawImage(Image, int, int, ImageObserver) at :570-592:
if (!r.isEmpty()) {
final List<Rectangle> regions = clipToPaint(r);
for (Rectangle region : regions) {
surface.drawCompatibleRaster(rast, region.x - tx, region.y - ty,
region.x, region.y, region.width, region.height, bgcolor);
surface.update(region.x, region.y, region.width, region.height);
}
}tx/ty are the pre-clip transformed origin captured at :519 / :576, so each sub-rectangle's source offset stays correct. All drawImage overloads funnel into these two — the scaled ones at :636 / :684 delegate after AreaAveragingScaleFilter, the sub-rectangle ones at :273 / :336-408 after CropImageFilter.
paintClip is not consulted by clearRect (:125), copyArea (:155), fillRect, or SurfaceGraphics2D's own drawImage/draw/fill. Only image blitting is clipped.
SurfaceGraphics2D.fillRect (:1002-1055):
public void fillRect(int x, int y, int width, int height) {
if (paint == null || (paint instanceof Color && ((Color) paint).getAlpha() == 255)) {
simpleGraphics.fillRect(x, y, width, height); // fast: direct surface fillRect
} else {
x = x + simpleGraphics.origin.x;
y = y + simpleGraphics.origin.y;
BufferedImage img = getFillerImage(x, y, width, height);
drawImage(img, x, y, background, null); // slow: PaintContext -> image -> blit
}
}Previously only paint == null took the fast path, so any setPaint(new Color(r,g,b)) — which also propagates to simpleGraphics.setColor at :766-774 — took the slow route: getFillerImage (:1057-1081) builds a PaintContext over a hardcoded 800×600 device rect, rasterises per-pixel in Java, allocates a BufferedImage, and blits it. Now an opaque Color is treated as a solid fill. getFillerImage is still used for gradients and TexturePaint.
-
Deprecated path:
JNodeGraphics2D/AbstractSurfaceGraphics2Dare marked@deprecatedand may be removed -
Missing implementations:
JNodeSurfaceGraphics2Dhas stub implementations forclip(Shape),drawRenderedImage,drawRenderableImage,drawImage(AffineTransform),drawImage(BufferedImageOp),hit(),shear()— all emit debug messages viaUnsafe.debug -
getDeviceConfiguration()returnsnullinJNodeSurfaceGraphics2D -
Double buffering overhead: The current implementation renders to a
BufferedImagethen blits to the surface, adding a copy per update cycle -
Clip transformation:
BasicSurfaceGraphicshandles origin translation;JNodeSurfaceGraphics2Dapplies anAffineTransformfrom the graphics origin for shape operations -
Lower z-order index means on top — the opposite of the usual intuition. Every occlusion test in
SwingToolkitandWindowBarusesotherZ < frameZto mean "in front" -
Paint clipping only applies to
SurfaceGraphics2D— theJNodeGraphics2Dbranch silently dropssetPaintClip -
Paint clipping only applies to
drawImage—fillRect,draw,clearRectandcopyAreaare not clipped -
getFillerImageassumes 800×600 — a hardcoded device rect that is wrong at any other resolution -
Unclamped
drawPixels/fillRectcorrupts neighbouring scanlines — see Video-Driver-Architecture
-
gui/src/awt/org/jnode/awt/swingpeers/SwingToolkit.java—getWindowPaintRegions(),activateWindow(), AWT root repaint -
gui/src/awt/org/jnode/awt/swingpeers/SwingComponentPeer.java— per-componentgetGraphics()that installs the paint clip -
gui/src/awt/org/jnode/awt/util/AbstractBitmapGraphics.java—drawPixels/fillRectclipping that prevents framebuffer corruption -
gui/src/awt/org/jnode/awt/JNodeGenericPeer.java— the AWT peer base; creates Graphics2D instances viaGraphicsFactory -
gui/src/awt/org/jnode/awt/JNodeToolkit.java— providesgetGraphics()returning theAbstractSurface, color model, and font metrics -
gui/src/awt/org/jnode/awt/util/BasicSurfaceGraphics.java— simple Graphics implementation underlying both Graphics2D classes, and the owner ofpaintClip -
gui/src/awt/org/jnode/awt/util/AbstractSurfaceGraphics2D.java— deprecated base with direct surface rendering -
gui/src/driver/org/jnode/driver/video/— video driver layer withSurfaceinterface and implementations