blob: 93b1a6e65dc53f0f2a9934af78b210431a189d42 [file] [log] [blame]
/*
* 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 <jlong.h>
#include <jni_util.h>
#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 */