1 /*
2  * Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 #include <jlong.h>
27 #include <string.h>
28 
29 #include "sun_java2d_d3d_D3DPaints_MultiGradient.h"
30 
31 #include "D3DPaints.h"
32 #include "D3DContext.h"
33 #include "D3DRenderQueue.h"
34 #include "D3DSurfaceData.h"
35 
36 HRESULT
D3DPaints_ResetPaint(D3DContext * d3dc)37 D3DPaints_ResetPaint(D3DContext *d3dc)
38 {
39     jint pixel, paintState;
40     jubyte ea;
41     HRESULT res;
42 
43     J2dTraceLn(J2D_TRACE_INFO, "D3DPaints_ResetPaint");
44 
45     RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
46 
47     paintState = d3dc->GetPaintState();
48     J2dTraceLn1(J2D_TRACE_VERBOSE, "  state=%d", paintState);
49 
50     res = d3dc->UpdateState(STATE_OTHEROP);
51 
52     // disable current complex paint state, if necessary
53     if (paintState > PAINT_ALPHACOLOR) {
54         IDirect3DDevice9 *pd3dDevice = d3dc->Get3DDevice();
55         DWORD sampler = d3dc->useMask ? 1 : 0;
56 
57         d3dc->SetTexture(NULL, sampler);
58         pd3dDevice->SetSamplerState(sampler,
59                                     D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
60         pd3dDevice->SetSamplerState(sampler,
61                                     D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
62         pd3dDevice->SetTextureStageState(sampler,
63                                          D3DTSS_TEXCOORDINDEX, sampler);
64         res = pd3dDevice->SetTextureStageState(sampler,
65                                                D3DTSS_TEXTURETRANSFORMFLAGS,
66                                                D3DTTFF_DISABLE);
67 
68         if (paintState == PAINT_GRADIENT     ||
69             paintState == PAINT_LIN_GRADIENT ||
70             paintState == PAINT_RAD_GRADIENT)
71         {
72             res = pd3dDevice->SetPixelShader(NULL);
73         }
74     }
75 
76     // set each component of the current color state to the extra alpha
77     // value, which will effectively apply the extra alpha to each fragment
78     // in paint/texturing operations
79     ea = (jubyte)(d3dc->extraAlpha * 0xff + 0.5f);
80     pixel = (ea << 24) | (ea << 16) | (ea << 8) | (ea << 0);
81     d3dc->pVCacher->SetColor(pixel);
82     d3dc->useMask = JNI_FALSE;
83     d3dc->SetPaintState(-1);
84     return res;
85 }
86 
87 HRESULT
D3DPaints_SetColor(D3DContext * d3dc,jint pixel)88 D3DPaints_SetColor(D3DContext *d3dc, jint pixel)
89 {
90     HRESULT res = S_OK;
91 
92     J2dTraceLn1(J2D_TRACE_INFO, "D3DPaints_SetColor: pixel=%08x", pixel);
93 
94     RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
95 
96     // no need to reset the current op state here unless the paint
97     // state really needs to be changed
98     if (d3dc->GetPaintState() > PAINT_ALPHACOLOR) {
99         res = D3DPaints_ResetPaint(d3dc);
100     }
101 
102     d3dc->pVCacher->SetColor(pixel);
103     d3dc->useMask = JNI_FALSE;
104     d3dc->SetPaintState(PAINT_ALPHACOLOR);
105     return res;
106 }
107 
108 /************************* GradientPaint support ****************************/
109 
110 HRESULT
D3DPaints_SetGradientPaint(D3DContext * d3dc,jboolean useMask,jboolean cyclic,jdouble p0,jdouble p1,jdouble p3,jint pixel1,jint pixel2)111 D3DPaints_SetGradientPaint(D3DContext *d3dc,
112                            jboolean useMask, jboolean cyclic,
113                            jdouble p0, jdouble p1, jdouble p3,
114                            jint pixel1, jint pixel2)
115 {
116     IDirect3DDevice9 *pd3dDevice;
117     HRESULT res;
118 
119     J2dTraceLn(J2D_TRACE_INFO, "D3DPaints_SetGradientPaint");
120 
121     RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
122     D3DPaints_ResetPaint(d3dc);
123 
124 #if 0
125     /*
126      * REMIND: The following code represents the original fast gradient
127      *         implementation.  The problem is that it relies on LINEAR
128      *         texture filtering, which does not provide sufficient
129      *         precision on certain hardware (from ATI, notably), which
130      *         will cause visible banding (e.g. 64 shades of gray between
131      *         black and white, instead of the expected 256 shades.  For
132      *         correctness on such hardware, it is necessary to use a
133      *         shader-based approach that does not suffer from these
134      *         precision issues (see below).  This original implementation
135      *         is about 16x faster than software, whereas the shader-based
136      *         implementation is only about 4x faster than software (still
137      *         impressive).  For simplicity, we will always use the
138      *         shader-based version for now, but in the future we could
139      *         consider using the fast path for certain hardware (that does
140      *         not exhibit the problem) or provide a flag to allow developers
141      *         to control which path we take (for those that are less
142      *         concerned about quality).  Therefore, I'll leave this code
143      *         here (currently disabled) for future use.
144      */
145     D3DResource *pGradientTexRes;
146     IDirect3DTexture9 *pGradientTex;
147 
148     // this will initialize the gradient texture, if necessary
149     res = d3dc->GetResourceManager()->GetGradientTexture(&pGradientTexRes);
150     RETURN_STATUS_IF_FAILED(res);
151 
152     pGradientTex = pGradientTexRes->GetTexture();
153 
154     // update the texture containing the gradient colors
155     {
156         D3DLOCKED_RECT lockedRect;
157         res = pGradientTex->LockRect(0, &lockedRect, NULL, D3DLOCK_NOSYSLOCK);
158         RETURN_STATUS_IF_FAILED(res);
159         jint *pPix = (jint*)lockedRect.pBits;
160         pPix[0] = pixel1;
161         pPix[1] = pixel2;
162         pGradientTex->UnlockRect(0);
163     }
164 
165     DWORD sampler = useMask ? 1 : 0;
166     DWORD wrapMode = cyclic ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP;
167     d3dc->SetTexture(pGradientTex, sampler);
168     d3dc->UpdateTextureColorState(D3DTA_TEXTURE, sampler);
169 
170     pd3dDevice = d3dc->Get3DDevice();
171     pd3dDevice->SetSamplerState(sampler, D3DSAMP_ADDRESSU, wrapMode);
172     pd3dDevice->SetSamplerState(sampler, D3DSAMP_ADDRESSV, wrapMode);
173     pd3dDevice->SetSamplerState(sampler, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
174     pd3dDevice->SetSamplerState(sampler, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
175 
176     D3DMATRIX mt;
177     ZeroMemory(&mt, sizeof(mt));
178     mt._11 = (float)p0;
179     mt._21 = (float)p1;
180     mt._31 = (float)0.0;
181     mt._41 = (float)p3;
182     mt._12 = 0.0f;
183     mt._22 = 1.0f;
184     mt._32 = 0.0f;
185     mt._42 = 0.0f;
186     pd3dDevice->SetTransform(useMask ? D3DTS_TEXTURE1 : D3DTS_TEXTURE0, &mt);
187     pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXCOORDINDEX,
188                                      D3DTSS_TCI_CAMERASPACEPOSITION);
189     res = pd3dDevice->SetTextureStageState(sampler,
190                                      D3DTSS_TEXTURETRANSFORMFLAGS,
191                                      D3DTTFF_COUNT2);
192 #else
193     jfloat params[4];
194     jfloat color[4];
195     jint flags = 0;
196 
197     if (cyclic)  flags |= BASIC_GRAD_IS_CYCLIC;
198     if (useMask) flags |= BASIC_GRAD_USE_MASK;
199 
200     // locate/enable the shader program for the given flags
201     res = d3dc->EnableBasicGradientProgram(flags);
202     RETURN_STATUS_IF_FAILED(res);
203 
204     // update the "uniform" values
205     params[0] = (jfloat)p0;
206     params[1] = (jfloat)p1;
207     params[2] = (jfloat)p3;
208     params[3] = 0.0f; // unused
209     pd3dDevice = d3dc->Get3DDevice();
210     res = pd3dDevice->SetPixelShaderConstantF(0, params, 1);
211 
212     color[0] = ((pixel1 >> 16) & 0xff) / 255.0f; // r
213     color[1] = ((pixel1 >>  8) & 0xff) / 255.0f; // g
214     color[2] = ((pixel1 >>  0) & 0xff) / 255.0f; // b
215     color[3] = ((pixel1 >> 24) & 0xff) / 255.0f; // a
216     res = pd3dDevice->SetPixelShaderConstantF(1, color, 1);
217 
218     color[0] = ((pixel2 >> 16) & 0xff) / 255.0f; // r
219     color[1] = ((pixel2 >>  8) & 0xff) / 255.0f; // g
220     color[2] = ((pixel2 >>  0) & 0xff) / 255.0f; // b
221     color[3] = ((pixel2 >> 24) & 0xff) / 255.0f; // a
222     res = pd3dDevice->SetPixelShaderConstantF(2, color, 1);
223 
224     // set up texture coordinate transform with identity matrix, which
225     // will have the effect of passing the current window-space coordinates
226     // through to the TEXCOORD0/1 register used by the basic gradient
227     // pixel shader
228     DWORD sampler = useMask ? 1 : 0;
229     D3DMATRIX mt;
230     ZeroMemory(&mt, sizeof(mt));
231     mt._11 = 1.0f;
232     mt._21 = 0.0f;
233     mt._31 = 0.0f;
234     mt._41 = 0.0f;
235     mt._12 = 0.0f;
236     mt._22 = 1.0f;
237     mt._32 = 0.0f;
238     mt._42 = 0.0f;
239     pd3dDevice->SetTransform(useMask ? D3DTS_TEXTURE1 : D3DTS_TEXTURE0, &mt);
240     pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXCOORDINDEX,
241                                      D3DTSS_TCI_CAMERASPACEPOSITION);
242     pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXTURETRANSFORMFLAGS,
243                                      D3DTTFF_COUNT2);
244 #endif
245 
246     // pixel state has been set appropriately in D3DPaints_ResetPaint()
247     d3dc->useMask = useMask;
248     d3dc->SetPaintState(PAINT_GRADIENT);
249     return res;
250 }
251 
252 /************************** TexturePaint support ****************************/
253 
254 HRESULT
D3DPaints_SetTexturePaint(D3DContext * d3dc,jboolean useMask,jlong pSrcOps,jboolean filter,jdouble xp0,jdouble xp1,jdouble xp3,jdouble yp0,jdouble yp1,jdouble yp3)255 D3DPaints_SetTexturePaint(D3DContext *d3dc,
256                           jboolean useMask,
257                           jlong pSrcOps, jboolean filter,
258                           jdouble xp0, jdouble xp1, jdouble xp3,
259                           jdouble yp0, jdouble yp1, jdouble yp3)
260 {
261     D3DSDOps *srcOps = (D3DSDOps *)jlong_to_ptr(pSrcOps);
262     IDirect3DDevice9 *pd3dDevice;
263     HRESULT res;
264 
265     J2dTraceLn(J2D_TRACE_INFO, "D3DPaints_SetTexturePaint");
266 
267     RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
268     RETURN_STATUS_IF_NULL(srcOps, E_FAIL);
269     RETURN_STATUS_IF_NULL(srcOps->pResource, E_FAIL);
270     D3DPaints_ResetPaint(d3dc);
271 
272     DWORD sampler = useMask ? 1 : 0;
273     DWORD dwFilter = filter ? D3DTEXF_LINEAR : D3DTEXF_POINT;
274     res = d3dc->SetTexture(srcOps->pResource->GetTexture(), sampler);
275     d3dc->UpdateTextureColorState(D3DTA_TEXTURE, sampler);
276     pd3dDevice = d3dc->Get3DDevice();
277     pd3dDevice->SetSamplerState(sampler, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
278     pd3dDevice->SetSamplerState(sampler, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
279     pd3dDevice->SetSamplerState(sampler, D3DSAMP_MAGFILTER, dwFilter);
280     pd3dDevice->SetSamplerState(sampler, D3DSAMP_MINFILTER, dwFilter);
281 
282     D3DMATRIX mt;
283     ZeroMemory(&mt, sizeof(mt));
284 
285     // offset by a half texel to correctly map texels to pixels
286     //  m02 = tx * m00 + ty * m01 + m02;
287     //  m12 = tx * m10 + ty * m11 + m12;
288     jdouble tx = (1 / (2.0f * srcOps->pResource->GetDesc()->Width));
289     jdouble ty = (1 / (2.0f * srcOps->pResource->GetDesc()->Height));
290     xp3 = tx * xp0 + ty * xp1 + xp3;
291     yp3 = tx * yp0 + ty * yp1 + yp3;
292 
293     mt._11 = (float)xp0;
294     mt._21 = (float)xp1;
295     mt._31 = (float)0.0;
296     mt._41 = (float)xp3;
297     mt._12 = (float)yp0;
298     mt._22 = (float)yp1;
299     mt._32 = (float)0.0;
300     mt._42 = (float)yp3;
301     pd3dDevice->SetTransform(useMask ? D3DTS_TEXTURE1 : D3DTS_TEXTURE0, &mt);
302     pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXCOORDINDEX,
303                                      D3DTSS_TCI_CAMERASPACEPOSITION);
304     pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXTURETRANSFORMFLAGS,
305                                      D3DTTFF_COUNT2);
306 
307     // pixel state has been set appropriately in D3DPaints_ResetPaint()
308     d3dc->useMask = useMask;
309     d3dc->SetPaintState(PAINT_TEXTURE);
310     return res;
311 }
312 
313 /****************** Shared MultipleGradientPaint support ********************/
314 
315 /** Composes the given parameters as flags into the given flags variable.*/
316 #define COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear) \
317     do {                                                        \
318         flags |= ((cycleMethod) & MULTI_GRAD_CYCLE_METHOD);     \
319         if (large)   flags |= MULTI_GRAD_LARGE;                 \
320         if (useMask) flags |= MULTI_GRAD_USE_MASK;              \
321         if (linear)  flags |= MULTI_GRAD_LINEAR_RGB;            \
322     } while (0)
323 
324 /**
325  * The maximum number of gradient "stops" supported by the fragment shader
326  * and related code.  When the MULTI_GRAD_LARGE flag is set, we will use
327  * MAX_FRACTIONS_LARGE; otherwise, we use MAX_FRACTIONS_SMALL.  By having
328  * two separate values, we can have one highly optimized shader (SMALL) that
329  * supports only a few fractions/colors, and then another, less optimal
330  * shader that supports more stops.
331  */
332 #define MAX_FRACTIONS \
333     sun_java2d_d3d_D3DPaints_MultiGradient_MULTI_MAX_FRACTIONS_D3D
334 #define MAX_FRACTIONS_LARGE MAX_FRACTIONS
335 #define MAX_FRACTIONS_SMALL 4
336 
337 /**
338  * Called from the D3DPaints_SetLinear/RadialGradientPaint() methods
339  * in order to setup the fraction/color values that are common to both.
340  */
341 static HRESULT
D3DPaints_SetMultiGradientPaint(D3DContext * d3dc,jboolean useMask,jint numStops,void * pFractions,void * pPixels)342 D3DPaints_SetMultiGradientPaint(D3DContext *d3dc,
343                                 jboolean useMask, jint numStops,
344                                 void *pFractions, void *pPixels)
345 {
346     HRESULT res;
347     IDirect3DDevice9 *pd3dDevice;
348     IDirect3DTexture9 *pMultiGradientTex;
349     D3DResource *pMultiGradientTexRes;
350     jint maxFractions = (numStops > MAX_FRACTIONS_SMALL) ?
351         MAX_FRACTIONS_LARGE : MAX_FRACTIONS_SMALL;
352     jfloat stopVals[MAX_FRACTIONS * 4];
353     jfloat *fractions = (jfloat *)pFractions;
354     juint *pixels = (juint *)pPixels;
355     int i;
356     int fIndex = 0;
357 
358     pd3dDevice = d3dc->Get3DDevice();
359 
360     // update the "uniform" fractions and scale factors
361     for (i = 0; i < maxFractions; i++) {
362         stopVals[fIndex+0] = (i < numStops)   ?
363             fractions[i] : 0.0f;
364         stopVals[fIndex+1] = (i < numStops-1) ?
365             1.0f / (fractions[i+1] - fractions[i]) : 0.0f;
366         stopVals[fIndex+2] = 0.0f; // unused
367         stopVals[fIndex+3] = 0.0f; // unused
368         fIndex += 4;
369     }
370     pd3dDevice->SetPixelShaderConstantF(0, stopVals, maxFractions);
371 
372     // this will initialize the multi-gradient texture, if necessary
373     res = d3dc->GetResourceManager()->
374         GetMultiGradientTexture(&pMultiGradientTexRes);
375     RETURN_STATUS_IF_FAILED(res);
376 
377     pMultiGradientTex = pMultiGradientTexRes->GetTexture();
378 
379     // update the texture containing the gradient colors
380     D3DLOCKED_RECT lockedRect;
381     res = pMultiGradientTex->LockRect(0, &lockedRect, NULL, D3DLOCK_NOSYSLOCK);
382     RETURN_STATUS_IF_FAILED(res);
383 
384     juint *pPix = (juint*)lockedRect.pBits;
385     memcpy(pPix, pixels, numStops*sizeof(juint));
386     if (numStops < MAX_MULTI_GRADIENT_COLORS) {
387         // when we don't have enough colors to fill the entire
388         // color gradient, we have to replicate the last color
389         // in the right-most texel for the NO_CYCLE case where the
390         // texcoord is sometimes forced to 1.0
391         pPix[MAX_MULTI_GRADIENT_COLORS-1] = pixels[numStops-1];
392     }
393     pMultiGradientTex->UnlockRect(0);
394 
395     // set the gradient texture and update relevant state
396     DWORD sampler = useMask ? 1 : 0;
397     res = d3dc->SetTexture(pMultiGradientTex, sampler);
398     d3dc->UpdateTextureColorState(D3DTA_TEXTURE, sampler);
399     pd3dDevice->SetSamplerState(sampler, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
400     pd3dDevice->SetSamplerState(sampler, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
401     pd3dDevice->SetSamplerState(sampler, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
402     pd3dDevice->SetSamplerState(sampler, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
403 
404     // set up texture coordinate transform with identity matrix, which
405     // will have the effect of passing the current window-space coordinates
406     // through to the TEXCOORD0/1 register used by the multi-stop
407     // gradient pixel shader
408     D3DMATRIX mt;
409     ZeroMemory(&mt, sizeof(mt));
410     mt._11 = 1.0f;
411     mt._21 = 0.0f;
412     mt._31 = 0.0f;
413     mt._41 = 0.0f;
414     mt._12 = 0.0f;
415     mt._22 = 1.0f;
416     mt._32 = 0.0f;
417     mt._42 = 0.0f;
418     pd3dDevice->SetTransform(useMask ? D3DTS_TEXTURE1 : D3DTS_TEXTURE0, &mt);
419     pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXCOORDINDEX,
420                                      D3DTSS_TCI_CAMERASPACEPOSITION);
421     pd3dDevice->SetTextureStageState(sampler, D3DTSS_TEXTURETRANSFORMFLAGS,
422                                      D3DTTFF_COUNT2);
423     return res;
424 }
425 
426 /********************** LinearGradientPaint support *************************/
427 
428 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)429 D3DPaints_SetLinearGradientPaint(D3DContext *d3dc, D3DSDOps *dstOps,
430                                  jboolean useMask, jboolean linear,
431                                  jint cycleMethod, jint numStops,
432                                  jfloat p0, jfloat p1, jfloat p3,
433                                  void *fractions, void *pixels)
434 {
435     HRESULT res;
436     IDirect3DDevice9 *pd3dDevice;
437     jfloat params[4];
438     jboolean large = (numStops > MAX_FRACTIONS_SMALL);
439     jint flags = 0;
440 
441     J2dTraceLn(J2D_TRACE_INFO, "D3DPaints_SetLinearGradientPaint");
442 
443     RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
444     RETURN_STATUS_IF_NULL(dstOps, E_FAIL);
445     D3DPaints_ResetPaint(d3dc);
446 
447     COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear);
448 
449     // locate/enable the shader program for the given flags
450     res = d3dc->EnableLinearGradientProgram(flags);
451     RETURN_STATUS_IF_FAILED(res);
452 
453     // update the common "uniform" values (fractions and colors)
454     D3DPaints_SetMultiGradientPaint(d3dc, useMask,
455                                     numStops, fractions, pixels);
456 
457     // update the other "uniform" values
458     params[0] = p0;
459     params[1] = p1;
460     params[2] = p3;
461     params[3] = 0.0f; // unused
462     pd3dDevice = d3dc->Get3DDevice();
463     res = pd3dDevice->SetPixelShaderConstantF(16, params, 1);
464 
465     // pixel state has been set appropriately in D3DPaints_ResetPaint()
466     d3dc->useMask = useMask;
467     d3dc->SetPaintState(PAINT_LIN_GRADIENT);
468     return res;
469 }
470 
471 /********************** RadialGradientPaint support *************************/
472 
473 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)474 D3DPaints_SetRadialGradientPaint(D3DContext *d3dc, D3DSDOps *dstOps,
475                                  jboolean useMask, jboolean linear,
476                                  jint cycleMethod, jint numStops,
477                                  jfloat m00, jfloat m01, jfloat m02,
478                                  jfloat m10, jfloat m11, jfloat m12,
479                                  jfloat focusX,
480                                  void *fractions, void *pixels)
481 {
482     HRESULT res;
483     IDirect3DDevice9 *pd3dDevice;
484     jfloat denom, inv_denom;
485     jfloat params[4];
486     jboolean large = (numStops > MAX_FRACTIONS_SMALL);
487     jint flags = 0;
488 
489     J2dTraceLn(J2D_TRACE_INFO, "D3DPaints_SetRadialGradientPaint");
490 
491     RETURN_STATUS_IF_NULL(d3dc, E_FAIL);
492     RETURN_STATUS_IF_NULL(dstOps, E_FAIL);
493     D3DPaints_ResetPaint(d3dc);
494 
495     COMPOSE_FLAGS(flags, cycleMethod, large, useMask, linear);
496 
497     // locate/enable the shader program for the given flags
498     res = d3dc->EnableRadialGradientProgram(flags);
499     RETURN_STATUS_IF_FAILED(res);
500 
501     // update the common "uniform" values (fractions and colors)
502     D3DPaints_SetMultiGradientPaint(d3dc, useMask,
503                                     numStops, fractions, pixels);
504 
505     // update the other "uniform" values
506     params[0] = m00;
507     params[1] = m01;
508     params[2] = m02;
509     params[3] = 0.0f; // unused
510     pd3dDevice = d3dc->Get3DDevice();
511     pd3dDevice->SetPixelShaderConstantF(16, params, 1);
512 
513     params[0] = m10;
514     params[1] = m11;
515     params[2] = m12;
516     params[3] = 0.0f; // unused
517     pd3dDevice->SetPixelShaderConstantF(17, params, 1);
518 
519     // pack a few unrelated, precalculated values into a single float4
520     denom = 1.0f - (focusX * focusX);
521     inv_denom = 1.0f / denom;
522     params[0] = focusX;
523     params[1] = denom;
524     params[2] = inv_denom;
525     params[3] = 0.0f; // unused
526     res = pd3dDevice->SetPixelShaderConstantF(18, params, 1);
527 
528     // pixel state has been set appropriately in D3DPaints_ResetPaint()
529     d3dc->useMask = useMask;
530     d3dc->SetPaintState(PAINT_RAD_GRADIENT);
531     return res;
532 }
533