diff options
Diffstat (limited to 'src/windows/native/sun/java2d/d3d/D3DPaints.cpp')
-rw-r--r-- | src/windows/native/sun/java2d/d3d/D3DPaints.cpp | 532 |
1 files changed, 532 insertions, 0 deletions
diff --git a/src/windows/native/sun/java2d/d3d/D3DPaints.cpp b/src/windows/native/sun/java2d/d3d/D3DPaints.cpp new file mode 100644 index 000000000..f11049904 --- /dev/null +++ b/src/windows/native/sun/java2d/d3d/D3DPaints.cpp @@ -0,0 +1,532 @@ +/* + * Copyright 2007-2008 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.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; +} |