<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Windows-1252">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:"Yu Gothic";
panose-1:2 11 4 0 0 0 0 0 0 0;}
@font-face
{font-family:Aptos;
panose-1:2 11 0 4 2 2 2 2 2 4;}
@font-face
{font-family:"Iosevka Fixed SS16";
panose-1:2 0 5 9 3 0 0 0 0 4;}
@font-face
{font-family:"Times New Roman \(Body CS\)";
panose-1:2 11 6 4 2 2 2 2 2 4;}
@font-face
{font-family:Consolas;
panose-1:2 11 6 9 2 2 4 3 2 4;}
@font-face
{font-family:"\@Yu Gothic";
panose-1:2 11 4 0 0 0 0 0 0 0;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0in;
font-size:10.0pt;
font-family:"Aptos",sans-serif;}
span.EmailStyle19
{mso-style-type:personal-reply;
font-family:"Iosevka Fixed SS16";
color:windowtext;}
.MsoChpDefault
{mso-style-type:export-only;
font-size:10.0pt;
mso-ligatures:none;}
@page WordSection1
{size:8.5in 11.0in;
margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
{page:WordSection1;}
--></style>
</head>
<body lang="EN-US" link="#467886" vlink="#96607D" style="word-wrap:break-word">
<div class="WordSection1">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">The devil is in detail, but I like this proposal very much, especially if most of the machinery is already there.
<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">How would it work if one does enable the software pipeline? Or the headless platform?<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16"">-andy<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Iosevka Fixed SS16""><o:p> </o:p></span></p>
<div id="mail-editor-reference-message-container">
<div>
<div>
<div style="border:none;border-top:solid #B5C4DF 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal" style="margin-bottom:12.0pt"><b><span style="font-size:12.0pt;color:black">From:
</span></b><span style="font-size:12.0pt;color:black">openjfx-dev <openjfx-dev-retn@openjdk.org> on behalf of John Hendrikx <john.hendrikx@gmail.com><br>
<b>Date: </b>Friday, June 27, 2025 at 03:15<br>
<b>To: </b>openjfx-dev@openjdk.org <openjfx-dev@openjdk.org><br>
<b>Subject: </b>Proposal: Exposing a GraphicsContext-like API for WritableImage via Software Renderer<o:p></o:p></span></p>
</div>
<p>Hi list,<o:p></o:p></p>
<p>I'm exploring whether there is interest in exposing a (Canvas) GraphicsContext-like interface for WritableImage backed by the existing software rendering pipeline. Currently, JavaFX offers two main choices for drawing and pixel manipulation, each with different
trade-offs:<o:p></o:p></p>
<p>- Canvas provides rich drawing primitives via GraphicsContext, but offers no direct pixel access—requiring costly GPU readbacks (e.g., snapshot()).<br>
- WritableImage, on the other hand, allows direct pixel manipulation via PixelReader/PixelWriter, but has no built-in support for drawing operations like shapes, fills, or blending.<o:p></o:p></p>
<p>My proposal would combine the strengths of both:<o:p></o:p></p>
<p>- Expose drawing operations (shapes, fills, etc.) for WritableImage <br>
- Direct access to image data before or after rendering without GPU readbacks / snapshots<br>
- Reuse of JavaFX’s software rendering stack (SWGraphics, PiscesRenderer, etc.) without activating the software pipeline globally<o:p></o:p></p>
<p>I’ve successfully tested this approach in a non-modular FX application by accessing internal APIs from com.sun.prism.sw. With minor adjustments (--add-exports), it may also work in modular environments.<br>
<br>
Work needed to support this as a public API might include:<o:p></o:p></p>
<p>- Creating a new GraphicsContext-like interface ("DrawingContext" ?)<br>
- Exposing a method on WritableImage to obtain such a context<br>
- Optionally, refactoring Canvas' GraphicsContext to implement this new interface (method signatures are likely compatible)<br>
- Implementing the new interface on top of the software renderer<o:p></o:p></p>
<p>See the end of the post for a working example (assuming you place the code in the "com.sun.prism.sw" package and deal with the module restrictions). Note that you do not need to enable the software pipeline (and you don't want to either, as the whole point
is to remain GPU accelerated but have software renderer backed drawing primitives for images).<o:p></o:p></p>
<p>Any feedback appreciated!<o:p></o:p></p>
<p style="margin-bottom:12.0pt">--John<o:p></o:p></p>
<div>
<div>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">package</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> com.sun.prism.sw;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> com.sun.glass.ui.Screen;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> com.sun.glass.utils.NativeLibLoader;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> com.sun.pisces.JavaSurface;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> com.sun.pisces.PiscesRenderer;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> com.sun.pisces.RendererBase;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> com.sun.prism.paint.Color;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> java.nio.IntBuffer;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> javafx.application.Application;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> javafx.scene.Scene;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> javafx.scene.image.ImageView;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> javafx.scene.image.PixelWriter;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> javafx.scene.image.WritableImage;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> javafx.scene.layout.StackPane;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">import</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> javafx.stage.Stage;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">public</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black">
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">class</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> SWRendererExample {<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">static</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> {<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">NativeLibLoader.<i>loadLibrary</i>(</span><span style="font-size:11.0pt;font-family:Consolas;color:#2A00FF">"prism_sw"</span><span style="font-size:11.0pt;font-family:Consolas;color:black">);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">}<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">public</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black">
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">static</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black">
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">void</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> main(String[] args) {<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">Application.<i>launch</i>(App.</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">class</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black">);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">}<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">public</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black">
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">static</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black">
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">class</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> App
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">extends</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> Application {<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:#646464">@Override</span><span style="font-size:11.0pt;font-family:Consolas;color:black"><o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">public</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black">
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">void</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> start(Stage primaryStage) {<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">WritableImage writableImage =
<i>createImageWithSWPipeline</i>();<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">Scene scene =
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">new</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> Scene(</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">new</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black">
StackPane(</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">new</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> ImageView(writableImage)));<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">primaryStage.setScene(scene);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">primaryStage.show();<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">}<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">}<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">public</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black">
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">static</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> WritableImage createImageWithSWPipeline() {<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">int</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> width = 400;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">int</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> height = 300;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">SWResourceFactory resourceFactory =
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">new</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> SWResourceFactory(Screen.<i>getMainScreen</i>());<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">SWRTTexture texture =
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">new</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> SWRTTexture(resourceFactory, width, height);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">SWContext swContext =
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">new</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> SWContext(resourceFactory);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:#3F7F5F">// Set up a surface to draw on and create the renderer:</span><span style="font-size:11.0pt;font-family:Consolas;color:black"><o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">int</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black">[] backingArray =
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">new</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black">
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">int</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black">[width * height];<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">IntBuffer pixelBuffer = IntBuffer.<i>wrap</i>(backingArray);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">JavaSurface surface =
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">new</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> JavaSurface(backingArray, RendererBase.</span><span style="font-size:11.0pt;font-family:Consolas;color:#0000C0">TYPE_INT_ARGB_PRE</span><span style="font-size:11.0pt;font-family:Consolas;color:black">,
width, height);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">PiscesRenderer renderer =
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">new</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> PiscesRenderer(surface);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:#3F7F5F">// Create SWGraphics for drawing (software renderer)</span><span style="font-size:11.0pt;font-family:Consolas;color:black"><o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">SWGraphics swGraphics =
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">new</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> SWGraphics(texture, swContext, renderer);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">swGraphics.clear(Color.</span><span style="font-size:11.0pt;font-family:Consolas;color:#0000C0">WHITE</span><span style="font-size:11.0pt;font-family:Consolas;color:black">);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">swGraphics.setPaint(Color.</span><span style="font-size:11.0pt;font-family:Consolas;color:#0000C0">BLUE</span><span style="font-size:11.0pt;font-family:Consolas;color:black">);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">swGraphics.fillRect(50, 50, 100, 100);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">swGraphics.setPaint(Color.</span><span style="font-size:11.0pt;font-family:Consolas;color:#0000C0">RED</span><span style="font-size:11.0pt;font-family:Consolas;color:black">);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">swGraphics.fillEllipse(75, 75, 10, 20);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:#3F7F5F">// Take the result and place it in a writable image:</span><span style="font-size:11.0pt;font-family:Consolas;color:black"><o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">WritableImage writableImage =
</span><b><span style="font-size:11.0pt;font-family:Consolas;color:#0000A0">new</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> WritableImage(width, height);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">PixelWriter pw = writableImage.getPixelWriter();<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">pw.setPixels(0, 0, width, height, javafx.scene.image.PixelFormat.<i>getIntArgbPreInstance</i>(), pixelBuffer.array(), 0, width);<o:p></o:p></span></p>
<p style="margin:0in;background:white"><b><span style="font-size:11.0pt;font-family:Consolas;color:#7F0055">return</span></b><span style="font-size:11.0pt;font-family:Consolas;color:black"> writableImage;<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">}<o:p></o:p></span></p>
<p style="margin:0in;background:white"><span style="font-size:11.0pt;font-family:Consolas;color:black">}<o:p></o:p></span></p>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>