Summary
Add support to share pixel data buffer between WritableImages.
Problem
- WritableImage creates its own buffer to store pixel data. This buffer is not accessible to programmer and can not be shared between WritableImages.
- Rendering an externally created buffer to WritableImage requires copying of entire buffer or the modified part of the buffer into the WritableImage's internal pixel data buffer.
- This is a very costly operation for a frequently updating buffer.
Solution
- Create WritableImages which can share pixel data buffer.
- Use java.nio.Buffer to store the pixel data.
- Only java.nio.IntBuffer and java.nio.ByteBuffer are sufficient to handle different PixelFormats.
Specification
Added a new javafx.scene.image.PixelBuffer
class:
/**
* The {@code PixelBuffer} class represents pixel data that is constructed from
* a {@link Buffer java.nio.Buffer} supplied by the application.
* A {@link WritableImage} can use this {@code PixelBuffer} directly without copying the pixel data.
* This {@code PixelBuffer} can be shared among multiple {@code WritableImage}s.
* Pixel data should be stored either in an {@link IntBuffer} using a {@link PixelFormat} of type
* {@code INT_ARGB_PRE} or in a {@link ByteBuffer} using a {@link PixelFormat} of type {@code BYTE_BGRA_PRE}.
* When the {@code Buffer} is updated using the {@link #updateBuffer PixelBuffer.updateBuffer} method,
* all {@code WritableImage}s that were created using this {@code PixelBuffer} are redrawn.
* <p>
* Example code that shows how to create a {@code PixelBuffer}:
* <pre>{@code // Creating a PixelBuffer using BYTE_BGRA_PRE pixel format.
* ByteBuffer byteBuffer = ByteBuffer.allocateDirect(width * height * 4);
* PixelFormat<ByteBuffer> pixelFormat = PixelFormat.getByteBgraPreInstance();
* PixelBuffer<ByteBuffer> pixelBuffer = new PixelBuffer<>(width, height, byteBuffer, pixelFormat);
* Image img = new WritableImage(pixelBuffer);
*
* // Creating a PixelBuffer using INT_ARGB_PRE pixel format.
* IntBuffer intBuffer = IntBuffer.allocate(width * height);
* PixelFormat<IntBuffer> pixelFormat = PixelFormat.getIntArgbPreInstance();
* PixelBuffer<IntBuffer> pixelBuffer = new PixelBuffer<>(width, height, intBuffer, pixelFormat);
* Image img = new WritableImage(pixelBuffer);}</pre>
*
* @param <T> the type of {@code Buffer} that stores the pixel data.
* Only {@code ByteBuffer} and {@code IntBuffer} are supported.
* @see WritableImage#WritableImage(PixelBuffer)
* @since 13
*/
public class PixelBuffer<T extends Buffer> {...}
/**
* Constructs a {@code PixelBuffer} using the specified {@code Buffer} and {@code PixelFormat}.
* The type of the specified {@code PixelFormat} must be either {@code PixelFormat.Type.INT_ARGB_PRE}
* or {@code PixelFormat.Type.BYTE_BGRA_PRE}.
* <p>The constructor does not allocate memory to store the pixel data. The application must provide
* a buffer with sufficient memory for the combination of dimensions {@code (width, height)} and the type
* of {@code PixelFormat}. The {@code PixelFormat.Type.INT_ARGB_PRE} requires an {@code IntBuffer} with
* minimum capacity of {@code width * height}, and the {@code PixelFormat.Type.BYTE_BGRA_PRE} requires
* a {@code ByteBuffer} with minimum capacity of {@code width * height * 4}.
*
* @param width width in pixels of this {@code PixelBuffer}
* @param height height in pixels of this {@code PixelBuffer}
* @param buffer the buffer that stores the pixel data
* @param pixelFormat the format of pixels in the {@code buffer}
* @throws IllegalArgumentException if either {@code width} or {@code height}
* is negative or zero, or if the type of {@code pixelFormat}
* is unsupported, or if {@code buffer} does
* not have sufficient memory, or if the type of {@code buffer}
* and {@code pixelFormat} do not match
* @throws NullPointerException if {@code buffer} or {@code pixelFormat} is {@code null}
*/
public PixelBuffer(int width, int height, T buffer, PixelFormat<T> pixelFormat) {...}
/**
* Returns the {@code buffer} of this {@code PixelBuffer}.
*
* @return the {@code buffer} of this {@code PixelBuffer}
*/
public T getBuffer() {...}
/**
* Returns the {@code width} of this {@code PixelBuffer}.
*
* @return the {@code width} of this {@code PixelBuffer}
*/
public int getWidth() {...}
/**
* Returns the {@code height} of this {@code PixelBuffer}.
*
* @return the {@code height} of this {@code PixelBuffer}
*/
public int getHeight() {...}
/**
* Returns the {@code PixelFormat} of this {@code PixelBuffer}.
*
* @return the {@code PixelFormat} of this {@code PixelBuffer}
*/
public PixelFormat<T> getPixelFormat() {...}
/**
* Invokes the specified {@code Callback} method and updates the dirty region of
* all {@code WritableImage}s that were created using this {@code PixelBuffer}.
* The {@code Callback} method is expected to update the buffer and
* return a {@code Rectangle2D} that encloses the dirty region, or
* return {@code null} to indicate that the entire buffer is dirty.
* <p>This method must be called on the JavaFX Application Thread.
* <p>Example code that shows how to use this method:
* <pre>{@code Callback<PixelBuffer<ByteBuffer>, Rectangle2D> callback = pixelBuffer -> {
* ByteBuffer buffer = pixelBuffer.getBuffer();
* // Update the buffer.
* return new Rectangle2D(x, y, dirtyWidth, dirtyHeight);
* };
* pixelBuffer.updateBuffer(callback);}</pre>
*
* @param callback the {@code Callback} method that updates the buffer
* @throws IllegalStateException if this method is called on a thread
* other than the JavaFX Application Thread.
* @throws NullPointerException if {@code callback} is {@code null}
**/
public void updateBuffer(Callback<PixelBuffer<T>, Rectangle2D> callback) {...}
Added a new Constructor to WritableImage
class,
/**
* Constructs a {@code WritableImage} using the specified {@code PixelBuffer}.
* The {@code Buffer} provided by the {@code PixelBuffer} will be used
* directly as the pixel data for this image.
* The {@code PixelBuffer} can be shared by multiple {@code WritableImage}s.
* Images constructed this way are readable using {@code Image.getPixelReader()},
* but are not writable using {@code WritableImage.getPixelWriter()}.
*
* @param pixelBuffer the {@code PixelBuffer} used to construct this image
* @throws NullPointerException if {@code pixelBuffer} is {@code null}
* @since 13
*/
public WritableImage(@NamedArg("PixelBuffer") PixelBuffer<? extends Buffer> pixelBuffer) {...}
Modified the javadoc of WritableImage.getPixelWriter()
from:
/**
* This method returns a {@code PixelWriter} that provides access to
* write the pixels of the image.
*
* @return the {@code PixelWriter} for writing pixels to the image
*/
public final PixelWriter getPixelWriter() {...}
to:
/**
* This method returns a {@code PixelWriter} that provides access to
* write the pixels of the image. This method is not supported for
* images constructed using a {@code PixelBuffer}.
*
* @return the {@code PixelWriter} for writing pixels to the image
* @throws UnsupportedOperationException if this image was created using a {@code PixelBuffer}
*/
public final PixelWriter getPixelWriter() {...}
- csr of
-
JDK-8167148 Add native rendering support by supporting WritableImages backed by NIO ByteBuffers
- Resolved