/*
* Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package com.sun.javafx.sg.prism;
import com.sun.javafx.geom.transform.
BaseTransform;
import com.sun.prism.
CompositeMode;
import com.sun.prism.
Graphics;
import com.sun.prism.
RTTexture;
import com.sun.prism.
RenderTarget;
import com.sun.prism.
ResourceFactory;
import com.sun.prism.
Texture;
import com.sun.prism.paint.
Color;
import com.sun.prism.paint.
Paint;
/**
* @author Thor Johannesson
*/
public class
NGSubScene extends
NGNode {
// The Scene logical dimensions (pre-pixel scaling)
private float
slWidth,
slHeight;
// The scaled dimensions last used in the rtt
private double
lastScaledW,
lastScaledH;
private
RTTexture rtt;
// ressolveRTT is a temporary render target to "resolve" a msaa render buffer
// into a normal color render target.
// REMIND: resolveRTT could be a single shared scratch rtt
private
RTTexture resolveRTT = null;
private
NGNode root = null;
private boolean
renderSG = true;
// Depth and msaa are immutable states
private final boolean
depthBuffer;
private final boolean
msaa;
public
NGSubScene(boolean
depthBuffer, boolean
msaa) {
this.
depthBuffer =
depthBuffer;
this.
msaa =
msaa;
}
private
NGSubScene() {
this(false, false);
}
public void
setRoot(
NGNode root) {
this.
root =
root;
}
private
Paint fillPaint;
public void
setFillPaint(
Object paint) {
fillPaint = (
Paint)
paint;
}
private
NGCamera camera;
public void
setCamera(
NGCamera camera) {
this.
camera =
camera == null ?
NGCamera.
INSTANCE :
camera;
}
public void
setWidth(float
width) {
if (this.
slWidth !=
width) {
this.
slWidth =
width;
geometryChanged();
invalidateRTT();
}
}
public void
setHeight(float
height) {
if (this.
slHeight !=
height) {
this.
slHeight =
height;
geometryChanged();
invalidateRTT();
}
}
private
NGLightBase[]
lights;
public
NGLightBase[]
getLights() { return
lights; }
public void
setLights(
NGLightBase[]
lights) {
this.
lights =
lights;
}
public void
markContentDirty() {
visualsChanged();
}
@
Override
protected void
visualsChanged() {
renderSG = true;
super.visualsChanged();
}
@
Override
protected void
geometryChanged() {
renderSG = true;
super.geometryChanged();
}
private void
invalidateRTT() {
if (
rtt != null) {
// TODO as possibile optimization by keeping old rtt if SubScene
// becomes smaller
rtt.
dispose();
rtt = null;
}
}
@
Override
protected boolean
hasOverlappingContents() {
//TODO verify correctness
return false;
}
private boolean
isOpaque = false;
private void
applyBackgroundFillPaint(
Graphics g) {
isOpaque = true;
if (
fillPaint != null) {
if (
fillPaint instanceof
Color) {
Color fillColor = (
Color)
fillPaint;
isOpaque = (
fillColor.
getAlpha() >= 1.0);
g.
clear(
fillColor);
} else {
if (!
fillPaint.
isOpaque()) {
g.
clear();
isOpaque = false;
}
g.
setPaint(
fillPaint);
g.
fillRect(0, 0,
rtt.
getContentWidth(),
rtt.
getContentHeight());
}
} else {
isOpaque = false;
// Default is transparent
g.
clear();
}
}
@
Override
public void
renderForcedContent(
Graphics gOptional) {
root.
renderForcedContent(
gOptional);
}
private static double
hypot(double
x, double
y, double
z) {
return
Math.
sqrt(
x *
x +
y *
y +
z *
z);
}
// Allow the scaled size in pixels to vary by a distance approximately
// large enough to affect the sampling result in a LINEAR interpolation.
// If we move by 1/256th of a pixel from one color to the opposite color
// then in the worst case the sample value might change by +/- 1 bit.
static final double
THRESHOLD = 1.0 / 256.0;
@
Override
protected void
renderContent(
Graphics g) {
if (
slWidth <= 0.0 ||
slHeight <= 0.0) { return; }
BaseTransform txform =
g.
getTransformNoClone();
double
scaleX =
hypot(
txform.
getMxx(),
txform.
getMyx(),
txform.
getMzx());
double
scaleY =
hypot(
txform.
getMxy(),
txform.
getMyy(),
txform.
getMzy());
double
scaledW =
slWidth *
scaleX;
double
scaledH =
slHeight *
scaleY;
int
rtWidth = (int)
Math.
ceil(
scaledW -
THRESHOLD);
int
rtHeight = (int)
Math.
ceil(
scaledH -
THRESHOLD);
if (
Math.
max(
Math.
abs(
scaledW -
lastScaledW),
Math.
abs(
scaledH -
lastScaledH)) >
THRESHOLD) {
if (
rtt != null &&
(
rtWidth !=
rtt.
getContentWidth() ||
rtHeight !=
rtt.
getContentHeight()))
{
invalidateRTT();
}
renderSG = true;
lastScaledW =
scaledW;
lastScaledH =
scaledH;
}
if (
rtt != null) {
rtt.
lock();
if (
rtt.
isSurfaceLost()) {
renderSG = true;
rtt = null;
}
}
if (
renderSG || !
root.
isClean()) {
if (
rtt == null) {
ResourceFactory factory =
g.
getResourceFactory();
rtt =
factory.
createRTTexture(
rtWidth,
rtHeight,
Texture.
WrapMode.
CLAMP_TO_ZERO,
msaa);
}
Graphics rttGraphics =
rtt.
createGraphics();
rttGraphics.
scale((float)
scaleX, (float)
scaleY);
rttGraphics.
setLights(
lights);
rttGraphics.
setDepthBuffer(
depthBuffer);
if (
camera != null) {
rttGraphics.
setCamera(
camera);
}
applyBackgroundFillPaint(
rttGraphics);
root.
render(
rttGraphics);
root.
clearDirtyTree();
renderSG = false;
}
if (
msaa) {
int
x0 =
rtt.
getContentX();
int
y0 =
rtt.
getContentY();
int
x1 =
x0 +
rtWidth;
int
y1 =
y0 +
rtHeight;
if ((
isOpaque ||
g.
getCompositeMode() ==
CompositeMode.
SRC) &&
isDirectBlitTransform(
txform,
scaleX,
scaleY) &&
!
g.
isDepthTest())
{
// Round translation to closest pixel
int
tx = (int)(
txform.
getMxt() + 0.5);
int
ty = (int)(
txform.
getMyt() + 0.5);
// Blit SubScene directly to scene surface
// Intersect src and dst boundaries.
// On D3D if blit is called outside boundary it will draw
// nothing. Using intersect prevents that from occurring.
RenderTarget target =
g.
getRenderTarget();
int
dstX0 =
target.
getContentX() +
tx;
int
dstY0 =
target.
getContentY() +
ty;
int
dstX1 =
dstX0 +
rtWidth;
int
dstY1 =
dstY0 +
rtHeight;
int
dstW =
target.
getContentWidth();
int
dstH =
target.
getContentHeight();
int
dX =
dstX1 >
dstW ?
dstW -
dstX1 : 0;
int
dY =
dstY1 >
dstH ?
dstH -
dstY1 : 0;
g.
blit(
rtt, null,
x0,
y0,
x1 +
dX,
y1 +
dY,
dstX0,
dstY0,
dstX1 +
dX,
dstY1 +
dY);
} else {
if (
resolveRTT != null &&
(
resolveRTT.
getContentWidth() <
rtWidth ||
(
resolveRTT.
getContentHeight() <
rtHeight)))
{
// If msaa rtt is larger than resolve buffer, then dispose
resolveRTT.
dispose();
resolveRTT = null;
}
if (
resolveRTT != null) {
resolveRTT.
lock();
if (
resolveRTT.
isSurfaceLost()) {
resolveRTT = null;
}
}
if (
resolveRTT == null) {
resolveRTT =
g.
getResourceFactory().
createRTTexture(
rtWidth,
rtHeight,
Texture.
WrapMode.
CLAMP_TO_ZERO, false);
}
// We could potentially reuse g, but any transform in g would
// affect the blit...
resolveRTT.
createGraphics().
blit(
rtt,
resolveRTT,
x0,
y0,
x1,
y1,
x0,
y0,
x1,
y1);
g.
drawTexture(
resolveRTT, 0, 0, (float) (
rtWidth /
scaleX), (float) (
rtHeight /
scaleY),
0, 0,
rtWidth,
rtHeight);
resolveRTT.
unlock();
}
} else {
g.
drawTexture(
rtt, 0, 0, (float) (
rtWidth /
scaleX), (float) (
rtHeight /
scaleY),
0, 0,
rtWidth,
rtHeight);
}
rtt.
unlock();
}
private static boolean
isDirectBlitTransform(
BaseTransform tx, double
sx, double
sy) {
if (
sx == 1.0 &&
sy == 1.0) return
tx.
isTranslateOrIdentity();
if (!
tx.
is2D()) return false;
return (
tx.
getMxx() ==
sx &&
tx.
getMxy() == 0.0 &&
tx.
getMyx() == 0.0 &&
tx.
getMyy() ==
sy);
}
public
NGCamera getCamera() {
return
camera;
}
}