The testcase below is minimized Webnode code that converts RTTexture to com.sun.prism.Image. It dumps the texture into a ByteBuffer, then creates an Image from that buffer. On Windows this works fine, on Mac red and green channels get interchanged, i.e. cyan is painted instead of magenta. So this issue seems to be pipeline specific, though I didn't try ES2 on Windows.
The essential code is in the renderWithPattern() method. Both the texture and the image are reported to have the same format, BYTE_BGRA_PRE. Changing byte order does not make any difference.
import com.sun.javafx.geom.BaseBounds;
import com.sun.javafx.geom.transform.BaseTransform;
import com.sun.javafx.jmx.MXNodeAlgorithm;
import com.sun.javafx.jmx.MXNodeAlgorithmContext;
import com.sun.javafx.sg.PGNode;
import com.sun.javafx.sg.prism.NGGroup;
import com.sun.prism.Graphics;
import com.sun.prism.GraphicsPipeline;
import com.sun.prism.Image;
import com.sun.prism.RTTexture;
import com.sun.prism.ResourceFactory;
import com.sun.prism.paint.Color;
import com.sun.prism.paint.ImagePattern;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class CustomNodeApp extends Application {
final int WIDTH = 400;
final int HEIGHT = 300;
final Color COLOR = new Color(1f, 0f, 1f, 1f); // magenta
class CustomNode extends Node {
@Override public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) {
return null;
}
@Override protected PGNode impl_createPGNode() {
return new NGCustomNode();
}
@Override public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
bounds.deriveWithNewBounds(0, 0, 0, WIDTH, HEIGHT, 0);
tx.transform(bounds, bounds);
return bounds;
}
@Override protected boolean impl_computeContains(double localX, double localY) {
return true;
}
}
class NGCustomNode extends NGGroup {
@Override public boolean hasOverlappingContents() {
return false;
}
@Override protected void renderContent(Graphics g) {
// renderDirectly(g); // works fine
renderWithPattern(g); // renders cyan instead of magenta
}
void renderDirectly(Graphics g) {
g.clear(COLOR);
}
void renderWithPattern(Graphics g) {
// render scene to a texture
ResourceFactory f = GraphicsPipeline.getDefaultResourceFactory();
RTTexture txt = f.createRTTexture(1, HEIGHT);
txt.createGraphics().clear(COLOR);
System.out.println("texture format: " + txt.getPixelFormat());
// convert texture to an image
ByteBuffer buf = ByteBuffer.allocate(HEIGHT * 4);
buf.order(ByteOrder.nativeOrder()); // makes no difference
buf.rewind();
txt.readPixels(buf);
Image img = Image.fromByteBgraPreData(buf, 1, HEIGHT);
System.out.println("image format: " + img.getPixelFormat());
// create a pattern from the image and paint it
buf.rewind();
ImagePattern pattern = new ImagePattern(img, 0, 0, 1, HEIGHT, false);
g.setPaint(pattern);
g.fillRect(0, 0, WIDTH, HEIGHT);
}
}
@Override public void start(Stage stage) {
stage.setScene(new Scene(new Group(new CustomNode())));
stage.sizeToScene();
stage.show();
}
public static void main(String[] args) {
launch();
}
}
The essential code is in the renderWithPattern() method. Both the texture and the image are reported to have the same format, BYTE_BGRA_PRE. Changing byte order does not make any difference.
import com.sun.javafx.geom.BaseBounds;
import com.sun.javafx.geom.transform.BaseTransform;
import com.sun.javafx.jmx.MXNodeAlgorithm;
import com.sun.javafx.jmx.MXNodeAlgorithmContext;
import com.sun.javafx.sg.PGNode;
import com.sun.javafx.sg.prism.NGGroup;
import com.sun.prism.Graphics;
import com.sun.prism.GraphicsPipeline;
import com.sun.prism.Image;
import com.sun.prism.RTTexture;
import com.sun.prism.ResourceFactory;
import com.sun.prism.paint.Color;
import com.sun.prism.paint.ImagePattern;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class CustomNodeApp extends Application {
final int WIDTH = 400;
final int HEIGHT = 300;
final Color COLOR = new Color(1f, 0f, 1f, 1f); // magenta
class CustomNode extends Node {
@Override public Object impl_processMXNode(MXNodeAlgorithm alg, MXNodeAlgorithmContext ctx) {
return null;
}
@Override protected PGNode impl_createPGNode() {
return new NGCustomNode();
}
@Override public BaseBounds impl_computeGeomBounds(BaseBounds bounds, BaseTransform tx) {
bounds.deriveWithNewBounds(0, 0, 0, WIDTH, HEIGHT, 0);
tx.transform(bounds, bounds);
return bounds;
}
@Override protected boolean impl_computeContains(double localX, double localY) {
return true;
}
}
class NGCustomNode extends NGGroup {
@Override public boolean hasOverlappingContents() {
return false;
}
@Override protected void renderContent(Graphics g) {
// renderDirectly(g); // works fine
renderWithPattern(g); // renders cyan instead of magenta
}
void renderDirectly(Graphics g) {
g.clear(COLOR);
}
void renderWithPattern(Graphics g) {
// render scene to a texture
ResourceFactory f = GraphicsPipeline.getDefaultResourceFactory();
RTTexture txt = f.createRTTexture(1, HEIGHT);
txt.createGraphics().clear(COLOR);
System.out.println("texture format: " + txt.getPixelFormat());
// convert texture to an image
ByteBuffer buf = ByteBuffer.allocate(HEIGHT * 4);
buf.order(ByteOrder.nativeOrder()); // makes no difference
buf.rewind();
txt.readPixels(buf);
Image img = Image.fromByteBgraPreData(buf, 1, HEIGHT);
System.out.println("image format: " + img.getPixelFormat());
// create a pattern from the image and paint it
buf.rewind();
ImagePattern pattern = new ImagePattern(img, 0, 0, 1, HEIGHT, false);
g.setPaint(pattern);
g.fillRect(0, 0, WIDTH, HEIGHT);
}
}
@Override public void start(Stage stage) {
stage.setScene(new Scene(new Group(new CustomNode())));
stage.sizeToScene();
stage.show();
}
public static void main(String[] args) {
launch();
}
}
- duplicates
-
JDK-8127686 Button Background is rendered as purple in JavaFx web view
- Closed