/*
* Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include <jlong.h>
#include <string.h>
#include "sun_java2d_d3d_D3DPaints_MultiGradient.h"
#include "D3DPaints.h"
#include "D3DContext.h"
#include "D3DRenderQueue.h"
#include "D3DSurfaceData.h"
HRESULT
D3DPaints_ResetPaint(D3DContext *d3dc)
{
jint pixel, paintState;
jubyte ea;
HRESULT res;
J2dTraceLn(J2D_TRACE_INFO, "D3DPaints_ResetPaint");
RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
paintState = d3dc->GetPaintState();
J2dTraceLn1(J2D_TRACE_VERBOSE, " state=%d", paintState);
res = d3dc->UpdateState(STATE_OTHEROP);
// disable current complex paint state, if necessary
if (paintState > PAINT_ALPHACOLOR) {
IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
DWORD sampler = d3dc->useMask ? 1 : 0;
d3dc->SetTexture(NULL, sampler);
pd3dDevice->SetSamplerState(sampler,
D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
pd3dDevice->SetSamplerState(sampler,
D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
pd3dDevice->SetTextureStageState(sampler,
D3DTSS_TEXCOORDINDEX, sampler);
res = pd3dDevice->SetTextureStageState(sampler,
D3DTSS_TEXTURETRANSFORMFLAGS,
D3DTTFF_DISABLE);
if (paintState == PAINT_GRADIENT ||
paintState == PAINT_LIN_GRADIENT ||
paintState == PAINT_RAD_GRADIENT)
{
res = pd3dDevice->SetPixelShader(NULL);
}
}
// set each component of the current color state to the extra alpha
// value, which will effectively apply the extra alpha to each fragment
// in paint/texturing operations
ea = (jubyte)(d3dc->extraAlpha * 0xff + 0.5f);
pixel = (ea << 24) | (ea << 16) | (ea << 8) | (ea << 0);
d3dc->pVCacher->SetColor(pixel);
d3dc->useMask = JNI_FALSE;
d3dc->SetPaintState(-1);
return res;
}
HRESULT
D3DPaints_SetColor(D3DContext *d3dc, jint pixel)
{
HRESULT res = S_OK;
J2dTraceLn1(J2D_TRACE_INFO, "D3DPaints_SetColor: pixel=%08x", pixel);
RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
// no need to reset the current op state here unless the paint
// state really needs to be changed
if (d3dc->GetPaintState() > PAINT_ALPHACOLOR) {
res = D3DPaints_ResetPaint(d3dc);
}
d3dc->pVCacher->SetColor(pixel);
d3dc->useMask = JNI_FALSE;
d3dc->SetPaintState(PAINT_ALPHACOLOR);
return res;
}
/************************* GradientPaint support ****************************/
HRESULT
D3DPaints_SetGradientPaint(D3DContext *d3dc,
jboolean useMask, jboolean cyclic,
jdouble p0, jdouble p1, jdouble p3,
jint pixel1, jint pixel2)
{
IDirect3DDevice9 *pd3dDevice;
HRESULT res;
J2dTraceLn(J2D_TRACE_INFO, "D3DPaints_SetGradientPaint");
RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
D3DPaints_ResetPaint(d3dc);
#if 0
/*
* REMIND: The following code represents the original fast gradient
* implementation. The problem is that it relies on LINEAR
* texture filtering, which does not provide sufficient
* precision on certain hardware (from ATI, notably), which
* will cause visible banding (e.g. 64 shades of gray between
* black and white, instead of the expected 256 shades. For
* correctness on such hardware, it is necessary to use a
* shader-based approach that does not suffer from these
* precision issues (see below). This original implementation
* is about 16x faster than software, whereas the shader-based
* implementation is only about 4x faster than software (still
* impressive). For simplicity, we will always use the
* shader-based version for now, but in the future we could
* consider using the fast path for certain hardware (that does
* not exhibit the problem) or provide a flag to allow developers
* to control which path we take (for those that are less
* concerned about quality). Therefore, I'll leave this code
* here (currently disabled) for future use.
*/
D3DResource *pGradientTexRes;
IDirect3DTexture9 *pGradientTex;
// this will initialize the gradient texture, if necessary
res = d3dc->GetResourceManager()->GetGradientTexture(&pGradientTexRes);
RETURN_STATUS_IF_FAILED(res);
pGradientTex = pGradientTexRes->GetTexture();
// update the texture containing the gradient colors
{
D3DLOCKED_RECT lockedRect;
res = pGradientTex->LockRect(0, &lockedRect, NULL, D3DLOCK_NOSYSLOCK);
RETURN_STATUS_IF_FAILED(res);
jint *pPix = (jint*)lockedRect.pBits;
pPix[0] = pixel1;
pPix[1] = pixel2;
pGradientTex->UnlockRect(0);
}
DWORD sampler = useMask ? 1 : 0;
DWORD wrapMode = cyclic ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP;
d3dc->SetTexture(pGradientTex, sampler);
d3dc->UpdateTextureColorState(D3DTA_TEXTURE, sampler);
pd3dDevice = d3dc->Get3DDevice();
pd3dDevice->SetSamplerState(sampler, D3DSAMP_ADDRESSU, wrapMode);
pd3dDevice->SetSamplerState(sampler, D3DSAMP_ADDRESSV, wrapMode);
pd3dDevice->SetSamplerState(sampler, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pd3dDevice->SetSamplerState(sampler, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
D3DMATRIX mt;
ZeroMemory(&mt, sizeof(mt));
mt._11 = (float)p0;
mt._21 = (float)p1;
mt._31 = (float)0.0;
mt._41 = (float)p3;
mt._12 = 0.0f;
mt._22 = 1.0f;
mt._32 = 0.0f;
mt._42 = 0.0f;
pd3dDevice->SetTransform(useMask ? D3DTS_TEXTURE1 : D3DTS_TEXTURE0, &mt);
pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXCOORDINDEX,
D3DTSS_TCI_CAMERASPACEPOSITION);
res = pd3dDevice->SetTextureStageState(sampler,
D3DTSS_TEXTURETRANSFORMFLAGS,
D3DTTFF_COUNT2);
#else
jfloat params[4];
jfloat color[4];
jint flags = 0;
if (cyclic) flags |= BASIC_GRAD_IS_CYCLIC;
if (useMask) flags |= BASIC_GRAD_USE_MASK;
// locate/enable the shader program for the given flags
res = d3dc->EnableBasicGradientProgram(flags);
RETURN_STATUS_IF_FAILED(res);
// update the "uniform" values
params[0] = (jfloat)p0;
params[1] = (jfloat)p1;
params[2] = (jfloat)p3;
params[3] = 0.0f; // unused
pd3dDevice = d3dc->Get3DDevice();
res = pd3dDevice->SetPixelShaderConstantF(0, params, 1);
color[0] = ((pixel1 >> 16) & 0xff) / 255.0f; // r
color[1] = ((pixel1 >> 8) & 0xff) / 255.0f; // g
color[2] = ((pixel1 >> 0) & 0xff) / 255.0f; // b
color[3] = ((pixel1 >> 24) & 0xff) / 255.0f; // a
res = pd3dDevice->SetPixelShaderConstantF(1, color, 1);
color[0] = ((pixel2 >> 16) & 0xff) / 255.0f; // r
color[1] = ((pixel2 >> 8) & 0xff) / 255.0f; // g
color[2] = ((pixel2 >> 0) & 0xff) / 255.0f; // b
color[3] = ((pixel2 >> 24) & 0xff) / 255.0f; // a
res = pd3dDevice->SetPixelShaderConstantF(2, color, 1);
// set up texture coordinate transform with identity matrix, which
// will have the effect of passing the current window-space coordinates
// through to the TEXCOORD0/1 register used by the basic gradient
// pixel shader
DWORD sampler = useMask ? 1 : 0;
D3DMATRIX mt;
ZeroMemory(&mt, sizeof(mt));
mt._11 = 1.0f;
mt._21 = 0.0f;
mt._31 = 0.0f;
mt._41 = 0.0f;
mt._12 = 0.0f;
mt._22 = 1.0f;
mt._32 = 0.0f;
mt._42 = 0.0f;
pd3dDevice->SetTransform(useMask ? D3DTS_TEXTURE1 : D3DTS_TEXTURE0, &mt);
pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXCOORDINDEX,
D3DTSS_TCI_CAMERASPACEPOSITION);
pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXTURETRANSFORMFLAGS,
D3DTTFF_COUNT2);
#endif
// pixel state has been set appropriately in D3DPaints_ResetPaint()
d3dc->useMask = useMask;
d3dc->SetPaintState(PAINT_GRADIENT);
return res;
}
/************************** TexturePaint support ****************************/
HRESULT
D3DPaints_SetTexturePaint(D3DContext *d3dc,
jboolean useMask,
jlong pSrcOps, jboolean filter,
jdouble xp0, jdouble xp1, jdouble xp3,
jdouble yp0, jdouble yp1, jdouble yp3)
{
D3DSDOps *srcOps = (D3DSDOps *)jlong_to_ptr(pSrcOps);
IDirect3DDevice9 *pd3dDevice;
HRESULT res;
J2dTraceLn(J2D_TRACE_INFO, "D3DPaints_SetTexturePaint");
RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
RETURN_STATUS_IF_NULL(srcOps, E_FAIL);
RETURN_STATUS_IF_NULL(srcOps->pResource, E_FAIL);
D3DPaints_ResetPaint(d3dc);
DWORD sampler = useMask ? 1 : 0;
DWORD dwFilter = filter ? D3DTEXF_LINEAR : D3DTEXF_POINT;
res = d3dc->SetTexture(srcOps->pResource->GetTexture(), sampler);
d3dc->UpdateTextureColorState(D3DTA_TEXTURE, sampler);
pd3dDevice = d3dc->Get3DDevice();
pd3dDevice->SetSamplerState(sampler, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
pd3dDevice->SetSamplerState(sampler, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
pd3dDevice->SetSamplerState(sampler, D3DSAMP_MAGFILTER, dwFilter);
pd3dDevice->SetSamplerState(sampler, D3DSAMP_MINFILTER, dwFilter);
D3DMATRIX mt;
ZeroMemory(&mt, sizeof(mt));
// offset by a half texel to correctly map texels to pixels
// m02 = tx * m00 + ty * m01 + m02;
// m12 = tx * m10 + ty * m11 + m12;
jdouble tx = (1 / (2.0f * srcOps->pResource->GetDesc()->Width));
jdouble ty = (1 / (2.0f * srcOps->pResource->GetDesc()->Height));
xp3 = tx * xp0 + ty * xp1 + xp3;
yp3 = tx * yp0 + ty * yp1 + yp3;
mt._11 = (float)xp0;
mt._21 = (float)xp1;
mt._31 = (float)0.0;
mt._41 = (float)xp3;
mt._12 = (float)yp0;
mt._22 = (float)yp1;
mt._32 = (float)0.0;
mt._42 = (float)yp3;
pd3dDevice->SetTransform(useMask ? D3DTS_TEXTURE1 : D3DTS_TEXTURE0, &mt);
pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXCOORDINDEX,
D3DTSS_TCI_CAMERASPACEPOSITION);
pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXTURETRANSFORMFLAGS,
D3DTTFF_COUNT2);
// pixel state has been set appropriately in D3DPaints_ResetPaint()
d3dc->useMask = useMask;
d3dc->SetPaintState(PAINT_TEXTURE);
return res;
}
/****************** Shared MultipleGradientPaint support ********************/
/** Composes the given parameters as flags into the given flags variable.*/
#define COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear) \
do { \
flags |= ((cycleMethod) & MULTI_GRAD_CYCLE_METHOD); \
if (large) flags |= MULTI_GRAD_LARGE; \
if (useMask) flags |= MULTI_GRAD_USE_MASK; \
if (linear) flags |= MULTI_GRAD_LINEAR_RGB; \
} while (0)
/**
* The maximum number of gradient "stops" supported by the fragment shader
* and related code. When the MULTI_GRAD_LARGE flag is set, we will use
* MAX_FRACTIONS_LARGE; otherwise, we use MAX_FRACTIONS_SMALL. By having
* two separate values, we can have one highly optimized shader (SMALL) that
* supports only a few fractions/colors, and then another, less optimal
* shader that supports more stops.
*/
#define MAX_FRACTIONS \
sun_java2d_d3d_D3DPaints_MultiGradient_MULTI_MAX_FRACTIONS_D3D
#define MAX_FRACTIONS_LARGE MAX_FRACTIONS
#define MAX_FRACTIONS_SMALL 4
/**
* Called from the D3DPaints_SetLinear/RadialGradientPaint() methods
* in order to setup the fraction/color values that are common to both.
*/
static HRESULT
D3DPaints_SetMultiGradientPaint(D3DContext *d3dc,
jboolean useMask, jint numStops,
void *pFractions, void *pPixels)
{
HRESULT res;
IDirect3DDevice9 *pd3dDevice;
IDirect3DTexture9 *pMultiGradientTex;
D3DResource *pMultiGradientTexRes;
jint maxFractions = (numStops > MAX_FRACTIONS_SMALL) ?
MAX_FRACTIONS_LARGE : MAX_FRACTIONS_SMALL;
jfloat stopVals[MAX_FRACTIONS * 4];
jfloat *fractions = (jfloat *)pFractions;
juint *pixels = (juint *)pPixels;
int i;
int fIndex = 0;
pd3dDevice = d3dc->Get3DDevice();
// update the "uniform" fractions and scale factors
for (i = 0; i < maxFractions; i++) {
stopVals[fIndex+0] = (i < numStops) ?
fractions[i] : 0.0f;
stopVals[fIndex+1] = (i < numStops-1) ?
1.0f / (fractions[i+1] - fractions[i]) : 0.0f;
stopVals[fIndex+2] = 0.0f; // unused
stopVals[fIndex+3] = 0.0f; // unused
fIndex += 4;
}
pd3dDevice->SetPixelShaderConstantF(0, stopVals, maxFractions);
// this will initialize the multi-gradient texture, if necessary
res = d3dc->GetResourceManager()->
GetMultiGradientTexture(&pMultiGradientTexRes);
RETURN_STATUS_IF_FAILED(res);
pMultiGradientTex = pMultiGradientTexRes->GetTexture();
// update the texture containing the gradient colors
D3DLOCKED_RECT lockedRect;
res = pMultiGradientTex->LockRect(0, &lockedRect, NULL, D3DLOCK_NOSYSLOCK);
RETURN_STATUS_IF_FAILED(res);
juint *pPix = (juint*)lockedRect.pBits;
memcpy(pPix, pixels, numStops*sizeof(juint));
if (numStops < MAX_MULTI_GRADIENT_COLORS) {
// when we don't have enough colors to fill the entire
// color gradient, we have to replicate the last color
// in the right-most texel for the NO_CYCLE case where the
// texcoord is sometimes forced to 1.0
pPix[MAX_MULTI_GRADIENT_COLORS-1] = pixels[numStops-1];
}
pMultiGradientTex->UnlockRect(0);
// set the gradient texture and update relevant state
DWORD sampler = useMask ? 1 : 0;
res = d3dc->SetTexture(pMultiGradientTex, sampler);
d3dc->UpdateTextureColorState(D3DTA_TEXTURE, sampler);
pd3dDevice->SetSamplerState(sampler, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
pd3dDevice->SetSamplerState(sampler, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
pd3dDevice->SetSamplerState(sampler, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pd3dDevice->SetSamplerState(sampler, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
// set up texture coordinate transform with identity matrix, which
// will have the effect of passing the current window-space coordinates
// through to the TEXCOORD0/1 register used by the multi-stop
// gradient pixel shader
D3DMATRIX mt;
ZeroMemory(&mt, sizeof(mt));
mt._11 = 1.0f;
mt._21 = 0.0f;
mt._31 = 0.0f;
mt._41 = 0.0f;
mt._12 = 0.0f;
mt._22 = 1.0f;
mt._32 = 0.0f;
mt._42 = 0.0f;
pd3dDevice->SetTransform(useMask ? D3DTS_TEXTURE1 : D3DTS_TEXTURE0, &mt);
pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXCOORDINDEX,
D3DTSS_TCI_CAMERASPACEPOSITION);
pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXTURETRANSFORMFLAGS,
D3DTTFF_COUNT2);
return res;
}
/********************** LinearGradientPaint support *************************/
HRESULT
D3DPaints_SetLinearGradientPaint(D3DContext *d3dc, D3DSDOps *dstOps,
jboolean useMask, jboolean linear,
jint cycleMethod, jint numStops,
jfloat p0, jfloat p1, jfloat p3,
void *fractions, void *pixels)
{
HRESULT res;
IDirect3DDevice9 *pd3dDevice;
jfloat params[4];
jboolean large = (numStops > MAX_FRACTIONS_SMALL);
jint flags = 0;
J2dTraceLn(J2D_TRACE_INFO, "D3DPaints_SetLinearGradientPaint");
RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
RETURN_STATUS_IF_NULL(dstOps, E_FAIL);
D3DPaints_ResetPaint(d3dc);
COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear);
// locate/enable the shader program for the given flags
res = d3dc->EnableLinearGradientProgram(flags);
RETURN_STATUS_IF_FAILED(res);
// update the common "uniform" values (fractions and colors)
D3DPaints_SetMultiGradientPaint(d3dc, useMask,
numStops, fractions, pixels);
// update the other "uniform" values
params[0] = p0;
params[1] = p1;
params[2] = p3;
params[3] = 0.0f; // unused
pd3dDevice = d3dc->Get3DDevice();
res = pd3dDevice->SetPixelShaderConstantF(16, params, 1);
// pixel state has been set appropriately in D3DPaints_ResetPaint()
d3dc->useMask = useMask;
d3dc->SetPaintState(PAINT_LIN_GRADIENT);
return res;
}
/********************** RadialGradientPaint support *************************/
HRESULT
D3DPaints_SetRadialGradientPaint(D3DContext *d3dc, D3DSDOps *dstOps,
jboolean useMask, jboolean linear,
jint cycleMethod, jint numStops,
jfloat m00, jfloat m01, jfloat m02,
jfloat m10, jfloat m11, jfloat m12,
jfloat focusX,
void *fractions, void *pixels)
{
HRESULT res;
IDirect3DDevice9 *pd3dDevice;
jfloat denom, inv_denom;
jfloat params[4];
jboolean large = (numStops > MAX_FRACTIONS_SMALL);
jint flags = 0;
J2dTraceLn(J2D_TRACE_INFO, "D3DPaints_SetRadialGradientPaint");
RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
RETURN_STATUS_IF_NULL(dstOps, E_FAIL);
D3DPaints_ResetPaint(d3dc);
COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear);
// locate/enable the shader program for the given flags
res = d3dc->EnableRadialGradientProgram(flags);
RETURN_STATUS_IF_FAILED(res);
// update the common "uniform" values (fractions and colors)
D3DPaints_SetMultiGradientPaint(d3dc, useMask,
numStops, fractions, pixels);
// update the other "uniform" values
params[0] = m00;
params[1] = m01;
params[2] = m02;
params[3] = 0.0f; // unused
pd3dDevice = d3dc->Get3DDevice();
pd3dDevice->SetPixelShaderConstantF(16, params, 1);
params[0] = m10;
params[1] = m11;
params[2] = m12;
params[3] = 0.0f; // unused
pd3dDevice->SetPixelShaderConstantF(17, params, 1);
// pack a few unrelated, precalculated values into a single float4
denom = 1.0f - (focusX * focusX);
inv_denom = 1.0f / denom;
params[0] = focusX;
params[1] = denom;
params[2] = inv_denom;
params[3] = 0.0f; // unused
res = pd3dDevice->SetPixelShaderConstantF(18, params, 1);
// pixel state has been set appropriately in D3DPaints_ResetPaint()
d3dc->useMask = useMask;
d3dc->SetPaintState(PAINT_RAD_GRADIENT);
return res;
}