diff options
Diffstat (limited to 'src/share/native/sun/java2d/opengl/OGLRenderer.c')
-rw-r--r-- | src/share/native/sun/java2d/opengl/OGLRenderer.c | 485 |
1 files changed, 484 insertions, 1 deletions
diff --git a/src/share/native/sun/java2d/opengl/OGLRenderer.c b/src/share/native/sun/java2d/opengl/OGLRenderer.c index 93b1a6e65..ac844b3eb 100644 --- a/src/share/native/sun/java2d/opengl/OGLRenderer.c +++ b/src/share/native/sun/java2d/opengl/OGLRenderer.c @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-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 @@ -27,6 +27,7 @@ #include <jlong.h> #include <jni_util.h> +#include <math.h> #include "sun_java2d_opengl_OGLRenderer.h" @@ -326,4 +327,486 @@ OGLRenderer_FillSpans(OGLContext *oglc, jint spanCount, jint *spans) } } +#define FILL_PGRAM(fx11, fy11, dx21, dy21, dx12, dy12) \ + do { \ + j2d_glVertex2f(fx11, fy11); \ + j2d_glVertex2f(fx11 + dx21, fy11 + dy21); \ + j2d_glVertex2f(fx11 + dx21 + dx12, fy11 + dy21 + dy12); \ + j2d_glVertex2f(fx11 + dx12, fy11 + dy12); \ + } while (0) + +void +OGLRenderer_FillParallelogram(OGLContext *oglc, + jfloat fx11, jfloat fy11, + jfloat dx21, jfloat dy21, + jfloat dx12, jfloat dy12) +{ + J2dTraceLn6(J2D_TRACE_INFO, + "OGLRenderer_FillParallelogram " + "(x=%6.2f y=%6.2f " + "dx1=%6.2f dy1=%6.2f " + "dx2=%6.2f dy2=%6.2f)", + fx11, fy11, + dx21, dy21, + dx12, dy12); + + RETURN_IF_NULL(oglc); + + CHECK_PREVIOUS_OP(GL_QUADS); + + FILL_PGRAM(fx11, fy11, dx21, dy21, dx12, dy12); +} + +void +OGLRenderer_DrawParallelogram(OGLContext *oglc, + jfloat fx11, jfloat fy11, + jfloat dx21, jfloat dy21, + jfloat dx12, jfloat dy12, + jfloat lwr21, jfloat lwr12) +{ + // dx,dy for line width in the "21" and "12" directions. + jfloat ldx21 = dx21 * lwr21; + jfloat ldy21 = dy21 * lwr21; + jfloat ldx12 = dx12 * lwr12; + jfloat ldy12 = dy12 * lwr12; + + // calculate origin of the outer parallelogram + jfloat ox11 = fx11 - (ldx21 + ldx12) / 2.0f; + jfloat oy11 = fy11 - (ldy21 + ldy12) / 2.0f; + + J2dTraceLn8(J2D_TRACE_INFO, + "OGLRenderer_DrawParallelogram " + "(x=%6.2f y=%6.2f " + "dx1=%6.2f dy1=%6.2f lwr1=%6.2f " + "dx2=%6.2f dy2=%6.2f lwr2=%6.2f)", + fx11, fy11, + dx21, dy21, lwr21, + dx12, dy12, lwr12); + + RETURN_IF_NULL(oglc); + + CHECK_PREVIOUS_OP(GL_QUADS); + + // Only need to generate 4 quads if the interior still + // has a hole in it (i.e. if the line width ratio was + // less than 1.0) + if (lwr21 < 1.0f && lwr12 < 1.0f) { + // Note: "TOP", "BOTTOM", "LEFT" and "RIGHT" here are + // relative to whether the dxNN variables are positive + // and negative. The math works fine regardless of + // their signs, but for conceptual simplicity the + // comments will refer to the sides as if the dxNN + // were all positive. "TOP" and "BOTTOM" segments + // are defined by the dxy21 deltas. "LEFT" and "RIGHT" + // segments are defined by the dxy12 deltas. + + // Each segment includes its starting corner and comes + // to just short of the following corner. Thus, each + // corner is included just once and the only lengths + // needed are the original parallelogram delta lengths + // and the "line width deltas". The sides will cover + // the following relative territories: + // + // T T T T T R + // L R + // L R + // L R + // L R + // L B B B B B + + // TOP segment, to left side of RIGHT edge + // "width" of original pgram, "height" of hor. line size + fx11 = ox11; + fy11 = oy11; + FILL_PGRAM(fx11, fy11, dx21, dy21, ldx12, ldy12); + + // RIGHT segment, to top of BOTTOM edge + // "width" of vert. line size , "height" of original pgram + fx11 = ox11 + dx21; + fy11 = oy11 + dy21; + FILL_PGRAM(fx11, fy11, ldx21, ldy21, dx12, dy12); + + // BOTTOM segment, from right side of LEFT edge + // "width" of original pgram, "height" of hor. line size + fx11 = ox11 + dx12 + ldx21; + fy11 = oy11 + dy12 + ldy21; + FILL_PGRAM(fx11, fy11, dx21, dy21, ldx12, ldy12); + + // LEFT segment, from bottom of TOP edge + // "width" of vert. line size , "height" of inner pgram + fx11 = ox11 + ldx12; + fy11 = oy11 + ldy12; + FILL_PGRAM(fx11, fy11, ldx21, ldy21, dx12, dy12); + } else { + // The line width ratios were large enough to consume + // the entire hole in the middle of the parallelogram + // so we can just issue one large quad for the outer + // parallelogram. + dx21 += ldx21; + dy21 += ldy21; + dx12 += ldx12; + dy12 += ldy12; + FILL_PGRAM(ox11, oy11, dx21, dy21, dx12, dy12); + } +} + +static GLhandleARB aaPgramProgram = 0; + +/* + * This shader fills the space between an outer and inner parallelogram. + * It can be used to draw an outline by specifying both inner and outer + * values. It fills pixels by estimating what portion falls inside the + * outer shape, and subtracting an estimate of what portion falls inside + * the inner shape. Specifying both inner and outer values produces a + * standard "wide outline". Specifying an inner shape that falls far + * outside the outer shape allows the same shader to fill the outer + * shape entirely since pixels that fall within the outer shape are never + * inside the inner shape and so they are filled based solely on their + * coverage of the outer shape. + * + * The setup code renders this shader over the bounds of the outer + * shape (or the only shape in the case of a fill operation) and + * sets the texture 0 coordinates so that 0,0=>0,1=>1,1=>1,0 in those + * texture coordinates map to the four corners of the parallelogram. + * Similarly the texture 1 coordinates map the inner shape to the + * unit square as well, but in a different coordinate system. + * + * When viewed in the texture coordinate systems the parallelograms + * we are filling are unit squares, but the pixels have then become + * tiny parallelograms themselves. Both of the texture coordinate + * systems are affine transforms so the rate of change in X and Y + * of the texture coordinates are essentially constants and happen + * to correspond to the size and direction of the slanted sides of + * the distorted pixels relative to the "square mapped" boundary + * of the parallelograms. + * + * The shader uses the dFdx() and dFdy() functions to measure the "rate + * of change" of these texture coordinates and thus gets an accurate + * measure of the size and shape of a pixel relative to the two + * parallelograms. It then uses the bounds of the size and shape + * of a pixel to intersect with the unit square to estimate the + * coverage of the pixel. Unfortunately, without a lot more work + * to calculate the exact area of intersection between a unit + * square (the original parallelogram) and a parallelogram (the + * distorted pixel), this shader only approximates the pixel + * coverage, but emperically the estimate is very useful and + * produces visually pleasing results, if not theoretically accurate. + */ +static const char *aaPgramShaderSource = + "void main() {" + // Calculate the vectors for the "legs" of the pixel parallelogram + // for the outer parallelogram. + " vec2 oleg1 = dFdx(gl_TexCoord[0].st);" + " vec2 oleg2 = dFdy(gl_TexCoord[0].st);" + // Calculate the bounds of the distorted pixel parallelogram. + " vec2 corner = gl_TexCoord[0].st - (oleg1+oleg2)/2.0;" + " vec2 omin = min(corner, corner+oleg1);" + " omin = min(omin, corner+oleg2);" + " omin = min(omin, corner+oleg1+oleg2);" + " vec2 omax = max(corner, corner+oleg1);" + " omax = max(omax, corner+oleg2);" + " omax = max(omax, corner+oleg1+oleg2);" + // Calculate the vectors for the "legs" of the pixel parallelogram + // for the inner parallelogram. + " vec2 ileg1 = dFdx(gl_TexCoord[1].st);" + " vec2 ileg2 = dFdy(gl_TexCoord[1].st);" + // Calculate the bounds of the distorted pixel parallelogram. + " corner = gl_TexCoord[1].st - (ileg1+ileg2)/2.0;" + " vec2 imin = min(corner, corner+ileg1);" + " imin = min(imin, corner+ileg2);" + " imin = min(imin, corner+ileg1+ileg2);" + " vec2 imax = max(corner, corner+ileg1);" + " imax = max(imax, corner+ileg2);" + " imax = max(imax, corner+ileg1+ileg2);" + // Clamp the bounds of the parallelograms to the unit square to + // estimate the intersection of the pixel parallelogram with + // the unit square. The ratio of the 2 rectangle areas is a + // reasonable estimate of the proportion of coverage. + " vec2 o1 = clamp(omin, 0.0, 1.0);" + " vec2 o2 = clamp(omax, 0.0, 1.0);" + " float oint = (o2.y-o1.y)*(o2.x-o1.x);" + " float oarea = (omax.y-omin.y)*(omax.x-omin.x);" + " vec2 i1 = clamp(imin, 0.0, 1.0);" + " vec2 i2 = clamp(imax, 0.0, 1.0);" + " float iint = (i2.y-i1.y)*(i2.x-i1.x);" + " float iarea = (imax.y-imin.y)*(imax.x-imin.x);" + // Proportion of pixel in outer shape minus the proportion + // of pixel in the inner shape == the coverage of the pixel + // in the area between the two. + " float coverage = oint/oarea - iint / iarea;" + " gl_FragColor = gl_Color * coverage;" + "}"; + +#define ADJUST_PGRAM(V1, DV, V2) \ + do { \ + if ((DV) >= 0) { \ + (V2) += (DV); \ + } else { \ + (V1) += (DV); \ + } \ + } while (0) + +// Invert the following transform: +// DeltaT(0, 0) == (0, 0) +// DeltaT(1, 0) == (DX1, DY1) +// DeltaT(0, 1) == (DX2, DY2) +// DeltaT(1, 1) == (DX1+DX2, DY1+DY2) +// TM00 = DX1, TM01 = DX2, (TM02 = X11) +// TM10 = DY1, TM11 = DY2, (TM12 = Y11) +// Determinant = TM00*TM11 - TM01*TM10 +// = DX1*DY2 - DX2*DY1 +// Inverse is: +// IM00 = TM11/det, IM01 = -TM01/det +// IM10 = -TM10/det, IM11 = TM00/det +// IM02 = (TM01 * TM12 - TM11 * TM02) / det, +// IM12 = (TM10 * TM02 - TM00 * TM12) / det, + +#define DECLARE_MATRIX(MAT) \ + jfloat MAT ## 00, MAT ## 01, MAT ## 02, MAT ## 10, MAT ## 11, MAT ## 12 + +#define GET_INVERTED_MATRIX(MAT, X11, Y11, DX1, DY1, DX2, DY2, RET_CODE) \ + do { \ + jfloat det = DX1*DY2 - DX2*DY1; \ + if (det == 0) { \ + RET_CODE; \ + } \ + MAT ## 00 = DY2/det; \ + MAT ## 01 = -DX2/det; \ + MAT ## 10 = -DY1/det; \ + MAT ## 11 = DX1/det; \ + MAT ## 02 = (DX2 * Y11 - DY2 * X11) / det; \ + MAT ## 12 = (DY1 * X11 - DX1 * Y11) / det; \ + } while (0) + +#define TRANSFORM(MAT, TX, TY, X, Y) \ + do { \ + TX = (X) * MAT ## 00 + (Y) * MAT ## 01 + MAT ## 02; \ + TY = (X) * MAT ## 10 + (Y) * MAT ## 11 + MAT ## 12; \ + } while (0) + +void +OGLRenderer_FillAAParallelogram(OGLContext *oglc, OGLSDOps *dstOps, + jfloat fx11, jfloat fy11, + jfloat dx21, jfloat dy21, + jfloat dx12, jfloat dy12) +{ + DECLARE_MATRIX(om); + // parameters for parallelogram bounding box + jfloat bx11, by11, bx22, by22; + // parameters for uv texture coordinates of parallelogram corners + jfloat u11, v11, u12, v12, u21, v21, u22, v22; + + J2dTraceLn6(J2D_TRACE_INFO, + "OGLRenderer_FillAAParallelogram " + "(x=%6.2f y=%6.2f " + "dx1=%6.2f dy1=%6.2f " + "dx2=%6.2f dy2=%6.2f)", + fx11, fy11, + dx21, dy21, + dx12, dy12); + + RETURN_IF_NULL(oglc); + RETURN_IF_NULL(dstOps); + + GET_INVERTED_MATRIX(om, fx11, fy11, dx21, dy21, dx12, dy12, + return); + + CHECK_PREVIOUS_OP(OGL_STATE_PGRAM_OP); + + bx11 = bx22 = fx11; + by11 = by22 = fy11; + ADJUST_PGRAM(bx11, dx21, bx22); + ADJUST_PGRAM(by11, dy21, by22); + ADJUST_PGRAM(bx11, dx12, bx22); + ADJUST_PGRAM(by11, dy12, by22); + bx11 = (jfloat) floor(bx11); + by11 = (jfloat) floor(by11); + bx22 = (jfloat) ceil(bx22); + by22 = (jfloat) ceil(by22); + + TRANSFORM(om, u11, v11, bx11, by11); + TRANSFORM(om, u21, v21, bx22, by11); + TRANSFORM(om, u12, v12, bx11, by22); + TRANSFORM(om, u22, v22, bx22, by22); + + j2d_glBegin(GL_QUADS); + j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, u11, v11); + j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 5.f, 5.f); + j2d_glVertex2f(bx11, by11); + j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, u21, v21); + j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 6.f, 5.f); + j2d_glVertex2f(bx22, by11); + j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, u22, v22); + j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 6.f, 6.f); + j2d_glVertex2f(bx22, by22); + j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, u12, v12); + j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 5.f, 6.f); + j2d_glVertex2f(bx11, by22); + j2d_glEnd(); +} + +void +OGLRenderer_FillAAParallelogramInnerOuter(OGLContext *oglc, OGLSDOps *dstOps, + jfloat ox11, jfloat oy11, + jfloat ox21, jfloat oy21, + jfloat ox12, jfloat oy12, + jfloat ix11, jfloat iy11, + jfloat ix21, jfloat iy21, + jfloat ix12, jfloat iy12) +{ + DECLARE_MATRIX(om); + DECLARE_MATRIX(im); + // parameters for parallelogram bounding box + jfloat bx11, by11, bx22, by22; + // parameters for uv texture coordinates of outer parallelogram corners + jfloat ou11, ov11, ou12, ov12, ou21, ov21, ou22, ov22; + // parameters for uv texture coordinates of inner parallelogram corners + jfloat iu11, iv11, iu12, iv12, iu21, iv21, iu22, iv22; + + RETURN_IF_NULL(oglc); + RETURN_IF_NULL(dstOps); + + GET_INVERTED_MATRIX(im, ix11, iy11, ix21, iy21, ix12, iy12, + // inner parallelogram is degenerate + // therefore it encloses no area + // fill outer + OGLRenderer_FillAAParallelogram(oglc, dstOps, + ox11, oy11, + ox21, oy21, + ox12, oy12); + return); + GET_INVERTED_MATRIX(om, ox11, oy11, ox21, oy21, ox12, oy12, + return); + + CHECK_PREVIOUS_OP(OGL_STATE_PGRAM_OP); + + bx11 = bx22 = ox11; + by11 = by22 = oy11; + ADJUST_PGRAM(bx11, ox21, bx22); + ADJUST_PGRAM(by11, oy21, by22); + ADJUST_PGRAM(bx11, ox12, bx22); + ADJUST_PGRAM(by11, oy12, by22); + bx11 = (jfloat) floor(bx11); + by11 = (jfloat) floor(by11); + bx22 = (jfloat) ceil(bx22); + by22 = (jfloat) ceil(by22); + + TRANSFORM(om, ou11, ov11, bx11, by11); + TRANSFORM(om, ou21, ov21, bx22, by11); + TRANSFORM(om, ou12, ov12, bx11, by22); + TRANSFORM(om, ou22, ov22, bx22, by22); + + TRANSFORM(im, iu11, iv11, bx11, by11); + TRANSFORM(im, iu21, iv21, bx22, by11); + TRANSFORM(im, iu12, iv12, bx11, by22); + TRANSFORM(im, iu22, iv22, bx22, by22); + + j2d_glBegin(GL_QUADS); + j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, ou11, ov11); + j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, iu11, iv11); + j2d_glVertex2f(bx11, by11); + j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, ou21, ov21); + j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, iu21, iv21); + j2d_glVertex2f(bx22, by11); + j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, ou22, ov22); + j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, iu22, iv22); + j2d_glVertex2f(bx22, by22); + j2d_glMultiTexCoord2fARB(GL_TEXTURE0_ARB, ou12, ov12); + j2d_glMultiTexCoord2fARB(GL_TEXTURE1_ARB, iu12, iv12); + j2d_glVertex2f(bx11, by22); + j2d_glEnd(); +} + +void +OGLRenderer_DrawAAParallelogram(OGLContext *oglc, OGLSDOps *dstOps, + jfloat fx11, jfloat fy11, + jfloat dx21, jfloat dy21, + jfloat dx12, jfloat dy12, + jfloat lwr21, jfloat lwr12) +{ + // dx,dy for line width in the "21" and "12" directions. + jfloat ldx21, ldy21, ldx12, ldy12; + // parameters for "outer" parallelogram + jfloat ofx11, ofy11, odx21, ody21, odx12, ody12; + // parameters for "inner" parallelogram + jfloat ifx11, ify11, idx21, idy21, idx12, idy12; + + J2dTraceLn8(J2D_TRACE_INFO, + "OGLRenderer_DrawAAParallelogram " + "(x=%6.2f y=%6.2f " + "dx1=%6.2f dy1=%6.2f lwr1=%6.2f " + "dx2=%6.2f dy2=%6.2f lwr2=%6.2f)", + fx11, fy11, + dx21, dy21, lwr21, + dx12, dy12, lwr12); + + RETURN_IF_NULL(oglc); + RETURN_IF_NULL(dstOps); + + // calculate true dx,dy for line widths from the "line width ratios" + ldx21 = dx21 * lwr21; + ldy21 = dy21 * lwr21; + ldx12 = dx12 * lwr12; + ldy12 = dy12 * lwr12; + + // calculate coordinates of the outer parallelogram + ofx11 = fx11 - (ldx21 + ldx12) / 2.0f; + ofy11 = fy11 - (ldy21 + ldy12) / 2.0f; + odx21 = dx21 + ldx21; + ody21 = dy21 + ldy21; + odx12 = dx12 + ldx12; + ody12 = dy12 + ldy12; + + // Only process the inner parallelogram if the line width ratio + // did not consume the entire interior of the parallelogram + // (i.e. if the width ratio was less than 1.0) + if (lwr21 < 1.0f && lwr12 < 1.0f) { + // calculate coordinates of the inner parallelogram + ifx11 = fx11 + (ldx21 + ldx12) / 2.0f; + ify11 = fy11 + (ldy21 + ldy12) / 2.0f; + idx21 = dx21 - ldx21; + idy21 = dy21 - ldy21; + idx12 = dx12 - ldx12; + idy12 = dy12 - ldy12; + + OGLRenderer_FillAAParallelogramInnerOuter(oglc, dstOps, + ofx11, ofy11, + odx21, ody21, + odx12, ody12, + ifx11, ify11, + idx21, idy21, + idx12, idy12); + } else { + OGLRenderer_FillAAParallelogram(oglc, dstOps, + ofx11, ofy11, + odx21, ody21, + odx12, ody12); + } +} + +void +OGLRenderer_EnableAAParallelogramProgram() +{ + J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_EnableAAParallelogramProgram"); + + if (aaPgramProgram == 0) { + aaPgramProgram = OGLContext_CreateFragmentProgram(aaPgramShaderSource); + if (aaPgramProgram == 0) { + J2dRlsTraceLn(J2D_TRACE_ERROR, + "OGLRenderer_EnableAAParallelogramProgram: " + "error creating program"); + return; + } + } + j2d_glUseProgramObjectARB(aaPgramProgram); +} + +void +OGLRenderer_DisableAAParallelogramProgram() +{ + J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_DisableAAParallelogramProgram"); + + j2d_glUseProgramObjectARB(0); +} + #endif /* !HEADLESS */ |