/* * Copyright 2003-2007 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. */ #ifndef HEADLESS #include #include #include "sun_java2d_opengl_OGLRenderer.h" #include "OGLRenderer.h" #include "OGLRenderQueue.h" #include "OGLSurfaceData.h" /** * Note: Some of the methods in this file apply a "magic number" * translation to line segments. The OpenGL specification lays out the * "diamond exit rule" for line rasterization, but it is loose enough to * allow for a wide range of line rendering hardware. (It appears that * some hardware, such as the Nvidia GeForce2 series, does not even meet * the spec in all cases.) As such it is difficult to find a mapping * between the Java2D and OpenGL line specs that works consistently across * all hardware combinations. * * Therefore the "magic numbers" you see here have been empirically derived * after testing on a variety of graphics hardware in order to find some * reasonable middle ground between the two specifications. The general * approach is to apply a fractional translation to vertices so that they * hit pixel centers and therefore touch the same pixels as in our other * pipelines. Emphasis was placed on finding values so that OGL lines with * a slope of +/- 1 hit all the same pixels as our other (software) loops. * The stepping in other diagonal lines rendered with OGL may deviate * slightly from those rendered with our software loops, but the most * important thing is that these magic numbers ensure that all OGL lines * hit the same endpoints as our software loops. * * If you find it necessary to change any of these magic numbers in the * future, just be sure that you test the changes across a variety of * hardware to ensure consistent rendering everywhere. */ void OGLRenderer_DrawLine(OGLContext *oglc, jint x1, jint y1, jint x2, jint y2) { J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_DrawLine"); RETURN_IF_NULL(oglc); CHECK_PREVIOUS_OP(GL_LINES); if (y1 == y2) { // horizontal GLfloat fx1 = (GLfloat)x1; GLfloat fx2 = (GLfloat)x2; GLfloat fy = ((GLfloat)y1) + 0.2f; if (x1 > x2) { GLfloat t = fx1; fx1 = fx2; fx2 = t; } j2d_glVertex2f(fx1+0.2f, fy); j2d_glVertex2f(fx2+1.2f, fy); } else if (x1 == x2) { // vertical GLfloat fx = ((GLfloat)x1) + 0.2f; GLfloat fy1 = (GLfloat)y1; GLfloat fy2 = (GLfloat)y2; if (y1 > y2) { GLfloat t = fy1; fy1 = fy2; fy2 = t; } j2d_glVertex2f(fx, fy1+0.2f); j2d_glVertex2f(fx, fy2+1.2f); } else { // diagonal GLfloat fx1 = (GLfloat)x1; GLfloat fy1 = (GLfloat)y1; GLfloat fx2 = (GLfloat)x2; GLfloat fy2 = (GLfloat)y2; if (x1 < x2) { fx1 += 0.2f; fx2 += 1.0f; } else { fx1 += 0.8f; fx2 -= 0.2f; } if (y1 < y2) { fy1 += 0.2f; fy2 += 1.0f; } else { fy1 += 0.8f; fy2 -= 0.2f; } j2d_glVertex2f(fx1, fy1); j2d_glVertex2f(fx2, fy2); } } void OGLRenderer_DrawRect(OGLContext *oglc, jint x, jint y, jint w, jint h) { J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_DrawRect"); if (w < 0 || h < 0) { return; } RETURN_IF_NULL(oglc); if (w < 2 || h < 2) { // If one dimension is less than 2 then there is no // gap in the middle - draw a solid filled rectangle. CHECK_PREVIOUS_OP(GL_QUADS); GLRECT_BODY_XYWH(x, y, w+1, h+1); } else { GLfloat fx1 = ((GLfloat)x) + 0.2f; GLfloat fy1 = ((GLfloat)y) + 0.2f; GLfloat fx2 = fx1 + ((GLfloat)w); GLfloat fy2 = fy1 + ((GLfloat)h); // Avoid drawing the endpoints twice. // Also prefer including the endpoints in the // horizontal sections which draw pixels faster. CHECK_PREVIOUS_OP(GL_LINES); // top j2d_glVertex2f(fx1, fy1); j2d_glVertex2f(fx2+1.0f, fy1); // right j2d_glVertex2f(fx2, fy1+1.0f); j2d_glVertex2f(fx2, fy2); // bottom j2d_glVertex2f(fx1, fy2); j2d_glVertex2f(fx2+1.0f, fy2); // left j2d_glVertex2f(fx1, fy1+1.0f); j2d_glVertex2f(fx1, fy2); } } void OGLRenderer_DrawPoly(OGLContext *oglc, jint nPoints, jint isClosed, jint transX, jint transY, jint *xPoints, jint *yPoints) { jboolean isEmpty = JNI_TRUE; jint mx, my; jint i; J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_DrawPoly"); if (xPoints == NULL || yPoints == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLRenderer_DrawPoly: points array is null"); return; } RETURN_IF_NULL(oglc); // Note that BufferedRenderPipe.drawPoly() has already rejected polys // with nPoints<2, so we can be certain here that we have nPoints>=2. mx = xPoints[0]; my = yPoints[0]; CHECK_PREVIOUS_OP(GL_LINE_STRIP); for (i = 0; i < nPoints; i++) { jint x = xPoints[i]; jint y = yPoints[i]; isEmpty = isEmpty && (x == mx && y == my); // Translate each vertex by a fraction so that we hit pixel centers. j2d_glVertex2f((GLfloat)(x + transX) + 0.5f, (GLfloat)(y + transY) + 0.5f); } if (isClosed && !isEmpty && (xPoints[nPoints-1] != mx || yPoints[nPoints-1] != my)) { // In this case, the polyline's start and end positions are // different and need to be closed manually; we do this by adding // one more segment back to the starting position. Note that we // do not need to fill in the last pixel (as we do in the following // block) because we are returning to the starting pixel, which // has already been filled in. j2d_glVertex2f((GLfloat)(mx + transX) + 0.5f, (GLfloat)(my + transY) + 0.5f); RESET_PREVIOUS_OP(); // so that we don't leave the line strip open } else if (!isClosed || isEmpty) { // OpenGL omits the last pixel in a polyline, so we fix this by // adding a one-pixel segment at the end. Also, if the polyline // never went anywhere (isEmpty is true), we need to use this // workaround to ensure that a single pixel is touched. CHECK_PREVIOUS_OP(GL_LINES); // this closes the line strip first mx = xPoints[nPoints-1] + transX; my = yPoints[nPoints-1] + transY; j2d_glVertex2i(mx, my); j2d_glVertex2i(mx+1, my+1); // no need for RESET_PREVIOUS_OP, as the line strip is no longer open } else { RESET_PREVIOUS_OP(); // so that we don't leave the line strip open } } JNIEXPORT void JNICALL Java_sun_java2d_opengl_OGLRenderer_drawPoly (JNIEnv *env, jobject oglr, jintArray xpointsArray, jintArray ypointsArray, jint nPoints, jboolean isClosed, jint transX, jint transY) { jint *xPoints, *yPoints; J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_drawPoly"); xPoints = (jint *) (*env)->GetPrimitiveArrayCritical(env, xpointsArray, NULL); if (xPoints != NULL) { yPoints = (jint *) (*env)->GetPrimitiveArrayCritical(env, ypointsArray, NULL); if (yPoints != NULL) { OGLContext *oglc = OGLRenderQueue_GetCurrentContext(); OGLRenderer_DrawPoly(oglc, nPoints, isClosed, transX, transY, xPoints, yPoints); // 6358147: reset current state, and ensure rendering is // flushed to dest if (oglc != NULL) { RESET_PREVIOUS_OP(); j2d_glFlush(); } (*env)->ReleasePrimitiveArrayCritical(env, ypointsArray, yPoints, JNI_ABORT); } (*env)->ReleasePrimitiveArrayCritical(env, xpointsArray, xPoints, JNI_ABORT); } } void OGLRenderer_DrawScanlines(OGLContext *oglc, jint scanlineCount, jint *scanlines) { J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_DrawScanlines"); RETURN_IF_NULL(oglc); RETURN_IF_NULL(scanlines); CHECK_PREVIOUS_OP(GL_LINES); while (scanlineCount > 0) { // Translate each vertex by a fraction so // that we hit pixel centers. GLfloat x1 = ((GLfloat)*(scanlines++)) + 0.2f; GLfloat x2 = ((GLfloat)*(scanlines++)) + 1.2f; GLfloat y = ((GLfloat)*(scanlines++)) + 0.5f; j2d_glVertex2f(x1, y); j2d_glVertex2f(x2, y); scanlineCount--; } } void OGLRenderer_FillRect(OGLContext *oglc, jint x, jint y, jint w, jint h) { J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_FillRect"); if (w <= 0 || h <= 0) { return; } RETURN_IF_NULL(oglc); CHECK_PREVIOUS_OP(GL_QUADS); GLRECT_BODY_XYWH(x, y, w, h); } void OGLRenderer_FillSpans(OGLContext *oglc, jint spanCount, jint *spans) { J2dTraceLn(J2D_TRACE_INFO, "OGLRenderer_FillSpans"); RETURN_IF_NULL(oglc); RETURN_IF_NULL(spans); CHECK_PREVIOUS_OP(GL_QUADS); while (spanCount > 0) { jint x1 = *(spans++); jint y1 = *(spans++); jint x2 = *(spans++); jint y2 = *(spans++); GLRECT_BODY_XYXY(x1, y1, x2, y2); spanCount--; } } #endif /* !HEADLESS */