Beispiel #1
0
/**
 * Enables the LCD text shader and updates any related state, such as the
 * gamma lookup table textures.
 */
static jboolean
OGLTR_EnableLCDGlyphModeState(GLuint glyphTextureID, jint contrast)
{
    // bind the texture containing glyph data to texture unit 0
    j2d_glActiveTextureARB(GL_TEXTURE0_ARB);
    j2d_glBindTexture(GL_TEXTURE_2D, glyphTextureID);

    // bind the texture tile containing destination data to texture unit 1
    j2d_glActiveTextureARB(GL_TEXTURE1_ARB);
    if (cachedDestTextureID == 0) {
        cachedDestTextureID =
            OGLContext_CreateBlitTexture(GL_RGB8, GL_RGB,
                                         OGLTR_CACHED_DEST_WIDTH,
                                         OGLTR_CACHED_DEST_HEIGHT);
        if (cachedDestTextureID == 0) {
            return JNI_FALSE;
        }
    }
    j2d_glBindTexture(GL_TEXTURE_2D, cachedDestTextureID);

    // note that GL_TEXTURE_2D was already enabled for texture unit 0,
    // but we need to explicitly enable it for texture unit 1
    j2d_glEnable(GL_TEXTURE_2D);

    // create the LCD text shader, if necessary
    if (lcdTextProgram == 0) {
        lcdTextProgram = OGLTR_CreateLCDTextProgram();
        if (lcdTextProgram == 0) {
            return JNI_FALSE;
        }
    }

    // enable the LCD text shader
    j2d_glUseProgramObjectARB(lcdTextProgram);

    // update the current contrast settings, if necessary
    if (lastLCDContrast != contrast) {
        if (!OGLTR_UpdateLCDTextContrast(contrast)) {
            return JNI_FALSE;
        }
        lastLCDContrast = contrast;
    }

    // update the current color settings
    if (!OGLTR_UpdateLCDTextColor(contrast)) {
        return JNI_FALSE;
    }

    // bind the gamma LUT textures
    j2d_glActiveTextureARB(GL_TEXTURE2_ARB);
    j2d_glBindTexture(GL_TEXTURE_3D, invGammaLutTextureID);
    j2d_glEnable(GL_TEXTURE_3D);
    j2d_glActiveTextureARB(GL_TEXTURE3_ARB);
    j2d_glBindTexture(GL_TEXTURE_3D, gammaLutTextureID);
    j2d_glEnable(GL_TEXTURE_3D);

    return JNI_TRUE;
}
Beispiel #2
0
/**
 * Updates the lookup table in the given texture object with the float
 * values in the given system memory buffer.  Note that we could use
 * glTexSubImage3D() when updating the texture after its first
 * initialization, but since we're updating the entire table (with
 * power-of-two dimensions) and this is a relatively rare event, we'll
 * just stick with glTexImage3D().
 */
static void
OGLTR_UpdateGammaLutTexture(GLuint texID, GLfloat *lut, jint size)
{
    j2d_glBindTexture(GL_TEXTURE_3D, texID);
    j2d_glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB8,
                     size, size, size, 0, GL_RGB, GL_FLOAT, lut);
}
/**
 * Makes a context current to the given source and destination
 * surfaces.  If there is a problem making the context current, this method
 * will return NULL; otherwise, returns a pointer to the OGLContext that is
 * associated with the destination surface.
 */
OGLContext *
OGLSD_MakeOGLContextCurrent(JNIEnv *env, OGLSDOps *srcOps, OGLSDOps *dstOps)
{
    GLXSDOps *dstGLXOps = (GLXSDOps *)dstOps->privOps;
    OGLContext *oglc;

    J2dTraceLn(J2D_TRACE_INFO, "OGLSD_MakeOGLContextCurrent");

    oglc = dstGLXOps->configData->glxInfo->context;
    if (oglc == NULL) {
        J2dRlsTraceLn(J2D_TRACE_ERROR,
                      "OGLSD_MakeOGLContextCurrent: context is null");
        return NULL;
    }

    if (dstOps->drawableType == OGLSD_FBOBJECT) {
        OGLContext *currentContext = OGLRenderQueue_GetCurrentContext();

        // first make sure we have a current context (if the context isn't
        // already current to some drawable, we will make it current to
        // its scratch surface)
        if (oglc != currentContext) {
            if (!GLXSD_MakeCurrentToScratch(env, oglc)) {
                return NULL;
            }
        }

        // now bind to the fbobject associated with the destination surface;
        // this means that all rendering will go into the fbobject destination
        // (note that we unbind the currently bound texture first; this is
        // recommended procedure when binding an fbobject)
        j2d_glBindTexture(dstOps->textureTarget, 0);
        j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dstOps->fbobjectID);
    } else {
        GLXSDOps *srcGLXOps = (GLXSDOps *)srcOps->privOps;
        GLXCtxInfo *ctxinfo = (GLXCtxInfo *)oglc->ctxInfo;

        // make the context current
        if (!j2d_glXMakeContextCurrent(awt_display,
                                       dstGLXOps->drawable,
                                       srcGLXOps->drawable,
                                       ctxinfo->context))
        {
            J2dRlsTraceLn(J2D_TRACE_ERROR,
                "OGLSD_MakeOGLContextCurrent: could not make current");
            return NULL;
        }

        if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) {
            // the GL_EXT_framebuffer_object extension is present, so we
            // must bind to the default (windowing system provided)
            // framebuffer
            j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
        }
    }

    return oglc;
}
Beispiel #4
0
/**
 * Initializes a 3D texture object for use as a three-dimensional gamma
 * lookup table.  Note that the wrap mode is initialized to GL_LINEAR so
 * that the table will interpolate adjacent values when the index falls
 * somewhere in between.
 */
static GLuint
OGLTR_InitGammaLutTexture()
{
    GLuint lutTextureID;

    j2d_glGenTextures(1, &lutTextureID);
    j2d_glBindTexture(GL_TEXTURE_3D, lutTextureID);
    j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    j2d_glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

    return lutTextureID;
}
Beispiel #5
0
/**
 * Initializes the one glyph cache (texture and data structure).
 * If lcdCache is JNI_TRUE, the texture will contain RGB data,
 * otherwise we will simply store the grayscale/monochrome glyph images
 * as intensity values (which work well with the GL_MODULATE function).
 */
static jboolean
OGLTR_InitGlyphCache(jboolean lcdCache)
{
    GlyphCacheInfo *gcinfo;
    GLclampf priority = 1.0f;
    GLenum internalFormat = lcdCache ? GL_RGB8 : GL_INTENSITY8;
    GLenum pixelFormat = lcdCache ? GL_RGB : GL_LUMINANCE;

    J2dTraceLn(J2D_TRACE_INFO, "OGLTR_InitGlyphCache");

    // init vertex cache (if it hasn't been already)
    if (!OGLVertexCache_InitVertexCache()) {
        return JNI_FALSE;
    }

    // init glyph cache data structure
    gcinfo = AccelGlyphCache_Init(OGLTR_CACHE_WIDTH,
                                  OGLTR_CACHE_HEIGHT,
                                  OGLTR_CACHE_CELL_WIDTH,
                                  OGLTR_CACHE_CELL_HEIGHT,
                                  OGLVertexCache_FlushVertexCache);
    if (gcinfo == NULL) {
        J2dRlsTraceLn(J2D_TRACE_ERROR,
                      "OGLTR_InitGlyphCache: could not init OGL glyph cache");
        return JNI_FALSE;
    }

    // init cache texture object
    j2d_glGenTextures(1, &gcinfo->cacheID);
    j2d_glBindTexture(GL_TEXTURE_2D, gcinfo->cacheID);
    j2d_glPrioritizeTextures(1, &gcinfo->cacheID, &priority);
    j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    j2d_glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,
                     OGLTR_CACHE_WIDTH, OGLTR_CACHE_HEIGHT, 0,
                     pixelFormat, GL_UNSIGNED_BYTE, NULL);

    cacheStatus = (lcdCache ? CACHE_LCD : CACHE_GRAY);
    glyphCache = gcinfo;

    return JNI_TRUE;
}
Beispiel #6
0
/**
 * Creates a 2D texture of the given format and dimensions and returns the
 * texture object identifier.  This method is typically used to create a
 * temporary texture for intermediate work, such as in the
 * OGLContext_InitBlitTileTexture() method below.
 */
GLuint
OGLContext_CreateBlitTexture(GLenum internalFormat, GLenum pixelFormat,
                             GLuint width, GLuint height)
{
    GLuint texID;
    GLint sp, sr, rl, align;
    GLclampf priority = 1.0f;

    J2dTraceLn(J2D_TRACE_INFO, "OGLContext_CreateBlitTexture");

    j2d_glGenTextures(1, &texID);
    j2d_glBindTexture(GL_TEXTURE_2D, texID);
    j2d_glPrioritizeTextures(1, &texID, &priority);
    j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    OGLSD_RESET_TEXTURE_WRAP(GL_TEXTURE_2D);

    // save pixel store parameters (since this method could be invoked after
    // the caller has already set up its pixel store parameters)
    j2d_glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &sp);
    j2d_glGetIntegerv(GL_UNPACK_SKIP_ROWS, &sr);
    j2d_glGetIntegerv(GL_UNPACK_ROW_LENGTH, &rl);
    j2d_glGetIntegerv(GL_UNPACK_ALIGNMENT, &align);

    // set pixel store parameters to default values
    j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
    j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
    j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    j2d_glTexImage2D(GL_TEXTURE_2D, 0, internalFormat,
                     width, height, 0,
                     pixelFormat, GL_UNSIGNED_BYTE, NULL);

    // restore pixel store parameters
    j2d_glPixelStorei(GL_UNPACK_SKIP_PIXELS, sp);
    j2d_glPixelStorei(GL_UNPACK_SKIP_ROWS, sr);
    j2d_glPixelStorei(GL_UNPACK_ROW_LENGTH, rl);
    j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, align);

    return texID;
}
Beispiel #7
0
void
OGLTR_EnableGlyphVertexCache(OGLContext *oglc)
{
    J2dTraceLn(J2D_TRACE_INFO, "OGLTR_EnableGlyphVertexCache");

    if (glyphCache == NULL) {
        if (!OGLTR_InitGlyphCache(JNI_FALSE)) {
            return;
        }
    }

    j2d_glEnable(GL_TEXTURE_2D);
    j2d_glBindTexture(GL_TEXTURE_2D, glyphCache->cacheID);
    j2d_glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    // for grayscale/monochrome text, the current OpenGL source color
    // is modulated with the glyph image as part of the texture
    // application stage, so we use GL_MODULATE here
    OGLC_UPDATE_TEXTURE_FUNCTION(oglc, GL_MODULATE);
}
Beispiel #8
0
/**
 * Returns JNI_TRUE only if all of the following conditions are met:
 *   - the GL_EXT_framebuffer_object extension is available
 *   - FBO support has been enabled via the system property
 *   - we can successfully create an FBO with depth capabilities
 */
static jboolean
OGLContext_IsFBObjectExtensionAvailable(JNIEnv *env,
                                        const char *extString)
{
    jboolean isFBObjectEnabled = JNI_FALSE;
    GLuint fbobjectID, textureID, depthID;
    jint width = 1, height = 1;

    J2dTraceLn(J2D_TRACE_INFO, "OGLContext_IsFBObjectExtensionAvailable");

    // first see if the fbobject extension is available
    if (!OGLContext_IsExtensionAvailable(extString,
                                         "GL_EXT_framebuffer_object"))
    {
        return JNI_FALSE;
    }

    // next see if the depth texture extension is available
    if (!OGLContext_IsExtensionAvailable(extString,
                                         "GL_ARB_depth_texture"))
    {
        return JNI_FALSE;
    }

    // next see if the fbobject system property has been enabled
    isFBObjectEnabled =
        JNU_GetStaticFieldByName(env, NULL,
                                 "sun/java2d/opengl/OGLSurfaceData",
                                 "isFBObjectEnabled", "Z").z;
    if (!isFBObjectEnabled) {
        J2dRlsTraceLn(J2D_TRACE_INFO,
                      "OGLContext_IsFBObjectExtensionAvailable: disabled via flag");
        return JNI_FALSE;
    }

    // finally, create a dummy fbobject with depth capabilities to see
    // if this configuration is supported by the drivers/hardware
    // (first we initialize a color texture object that will be used to
    // construct the dummy fbobject)
    j2d_glGenTextures(1, &textureID);
    j2d_glBindTexture(GL_TEXTURE_2D, textureID);
    j2d_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
                     width, height, 0,
                     GL_RGB, GL_UNSIGNED_BYTE, NULL);
    j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    j2d_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    // initialize framebuffer object using color texture created above
    if (!OGLSD_InitFBObject(&fbobjectID, &depthID,
                            textureID, GL_TEXTURE_2D,
                            width, height))
    {
        J2dRlsTraceLn(J2D_TRACE_INFO,
                      "OGLContext_IsFBObjectExtensionAvailable: fbobject unsupported");
        j2d_glDeleteTextures(1, &textureID);
        return JNI_FALSE;
    }

    // delete the temporary resources
    j2d_glDeleteTextures(1, &textureID);
    j2d_glDeleteRenderbuffersEXT(1, &depthID);
    j2d_glDeleteFramebuffersEXT(1, &fbobjectID);

    J2dRlsTraceLn(J2D_TRACE_INFO,
                  "OGLContext_IsFBObjectExtensionAvailable: fbobject supported");

    return JNI_TRUE;
}
Beispiel #9
0
/**
 * Makes a context current to the given source and destination
 * surfaces.  If there is a problem making the context current, this method
 * will return NULL; otherwise, returns a pointer to the OGLContext that is
 * associated with the destination surface.
 */
OGLContext *
OGLSD_MakeOGLContextCurrent(JNIEnv *env, OGLSDOps *srcOps, OGLSDOps *dstOps)
{
    WGLSDOps *srcWGLOps = (WGLSDOps *)srcOps->privOps;
    WGLSDOps *dstWGLOps = (WGLSDOps *)dstOps->privOps;
    OGLContext *oglc;
    WGLCtxInfo *ctxinfo;
    HDC srcHDC, dstHDC;
    BOOL success;

    J2dTraceLn(J2D_TRACE_INFO, "OGLSD_MakeOGLContextCurrent");

    J2dTraceLn4(J2D_TRACE_VERBOSE, "  src: %d %p dst: %d %p",
                srcOps->drawableType, srcOps,
                dstOps->drawableType, dstOps);

    oglc = dstWGLOps->configInfo->context;
    if (oglc == NULL) {
        J2dRlsTraceLn(J2D_TRACE_ERROR,
                      "OGLSD_MakeOGLContextCurrent: context is null");
        return NULL;
    }

    if (dstOps->drawableType == OGLSD_FBOBJECT) {
        OGLContext *currentContext = OGLRenderQueue_GetCurrentContext();

        // first make sure we have a current context (if the context isn't
        // already current to some drawable, we will make it current to
        // its scratch surface)
        if (oglc != currentContext) {
            if (!WGLSD_MakeCurrentToScratch(env, oglc)) {
                return NULL;
            }
        }

        // now bind to the fbobject associated with the destination surface;
        // this means that all rendering will go into the fbobject destination
        // (note that we unbind the currently bound texture first; this is
        // recommended procedure when binding an fbobject)
        j2d_glBindTexture(dstOps->textureTarget, 0);
        j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dstOps->fbobjectID);

        return oglc;
    }

    ctxinfo = (WGLCtxInfo *)oglc->ctxInfo;

    // get the hdc for the destination surface
    if (dstOps->drawableType == OGLSD_PBUFFER) {
        dstHDC = dstWGLOps->pbufferDC;
    } else {
        dstHDC = GetDC(dstWGLOps->window);
    }

    // get the hdc for the source surface
    if (srcOps->drawableType == OGLSD_PBUFFER) {
        srcHDC = srcWGLOps->pbufferDC;
    } else {
        // the source will always be equal to the destination in this case
        srcHDC = dstHDC;
    }

    // REMIND: in theory we should be able to use wglMakeContextCurrentARB()
    // even when the src/dst surfaces are the same, but this causes problems
    // on ATI's drivers (see 6525997); for now we will only use it when the
    // surfaces are different, otherwise we will use the old
    // wglMakeCurrent() approach...
    if (srcHDC != dstHDC) {
        // use WGL_ARB_make_current_read extension to make context current
        success =
            j2d_wglMakeContextCurrentARB(dstHDC, srcHDC, ctxinfo->context);
    } else {
        // use the old approach for making current to the destination
        success = j2d_wglMakeCurrent(dstHDC, ctxinfo->context);
    }
    if (!success) {
        J2dRlsTraceLn(J2D_TRACE_ERROR,
                      "OGLSD_MakeOGLContextCurrent: could not make current");
        if (dstOps->drawableType != OGLSD_PBUFFER) {
            ReleaseDC(dstWGLOps->window, dstHDC);
        }
        return NULL;
    }

    if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) {
        // the GL_EXT_framebuffer_object extension is present, so we
        // must bind to the default (windowing system provided)
        // framebuffer
        j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    }

    if (dstOps->drawableType != OGLSD_PBUFFER) {
        ReleaseDC(dstWGLOps->window, dstHDC);
    }

    return oglc;
}
Beispiel #10
0
/**
 * Used to track whether we are within a series of simple primitive operations
 * or texturing operations.  The op parameter determines the nature of the
 * operation that is to follow.  Valid values for this op parameter are:
 *
 *     GL_QUADS
 *     GL_LINES
 *     GL_LINE_LOOP
 *     GL_LINE_STRIP
 *     (basically any of the valid parameters for glBegin())
 *
 *     GL_TEXTURE_2D
 *     GL_TEXTURE_RECTANGLE_ARB
 *
 *     OGL_STATE_RESET
 *     OGL_STATE_CHANGE
 *     OGL_STATE_MASK_OP
 *     OGL_STATE_GLYPH_OP
 *
 * Note that the above constants are guaranteed to be unique values.  The
 * last few are defined to be negative values to differentiate them from
 * the core GL* constants, which are defined to be non-negative.
 *
 * For simple primitives, this method allows us to batch similar primitives
 * within the same glBegin()/glEnd() pair.  For example, if we have 100
 * consecutive FILL_RECT operations, we only have to call glBegin(GL_QUADS)
 * for the first op, and then subsequent operations will consist only of
 * glVertex*() calls, which helps improve performance.  The glEnd() call
 * only needs to be issued before an operation that cannot happen within a
 * glBegin()/glEnd() pair (e.g. updating the clip), or one that requires a
 * different primitive mode (e.g. GL_LINES).
 *
 * For operations that involve texturing, this method helps us to avoid
 * calling glEnable(GL_TEXTURE_2D) and glDisable(GL_TEXTURE_2D) around each
 * operation.  For example, if we have an alternating series of ISO_BLIT
 * and MASK_BLIT operations (both of which involve texturing), we need
 * only to call glEnable(GL_TEXTURE_2D) before the first ISO_BLIT operation.
 * The glDisable(GL_TEXTURE_2D) call only needs to be issued before an
 * operation that cannot (or should not) happen while texturing is enabled
 * (e.g. a context change, or a simple primitive operation like GL_QUADS).
 */
void
OGLRenderQueue_CheckPreviousOp(jint op)
{
    if (previousOp == op) {
        // The op is the same as last time, so we can return immediately.
        return;
    }

    J2dTraceLn1(J2D_TRACE_VERBOSE,
                "OGLRenderQueue_CheckPreviousOp: new op=%d", op);

    switch (previousOp) {
    case GL_TEXTURE_2D:
    case GL_TEXTURE_RECTANGLE_ARB:
        if (op == OGL_STATE_CHANGE) {
            // Optimization: Certain state changes (those marked as
            // OGL_STATE_CHANGE) are allowed while texturing is enabled.
            // In this case, we can allow previousOp to remain as it is and
            // then return early.
            return;
        } else {
            // Otherwise, op must be a primitive operation, or a reset, so
            // we will disable texturing.
            j2d_glDisable(previousOp);
            // This next step of binding to zero should not be strictly
            // necessary, but on some older Nvidia boards (e.g. GeForce 2)
            // problems will arise if GL_TEXTURE_2D and
            // GL_TEXTURE_RECTANGLE_ARB are bound at the same time, so we
            // will do this just to be safe.
            j2d_glBindTexture(previousOp, 0);
        }
        break;
    case OGL_STATE_MASK_OP:
        OGLVertexCache_DisableMaskCache(oglc);
        break;
    case OGL_STATE_GLYPH_OP:
        OGLTR_DisableGlyphVertexCache(oglc);
        break;
    case OGL_STATE_PGRAM_OP:
        OGLRenderer_DisableAAParallelogramProgram();
        break;
    case OGL_STATE_RESET:
    case OGL_STATE_CHANGE:
        // No-op
        break;
    default:
        // In this case, op must be one of:
        //     - the start of a different primitive type (glBegin())
        //     - a texturing operation
        //     - a state change (not allowed within glBegin()/glEnd() pairs)
        //     - a reset
        // so we must first complete the previous primitive operation.
        j2d_glEnd();
        break;
    }

    switch (op) {
    case GL_TEXTURE_2D:
    case GL_TEXTURE_RECTANGLE_ARB:
        // We are starting a texturing operation, so enable texturing.
        j2d_glEnable(op);
        break;
    case OGL_STATE_MASK_OP:
        OGLVertexCache_EnableMaskCache(oglc);
        break;
    case OGL_STATE_GLYPH_OP:
        OGLTR_EnableGlyphVertexCache(oglc);
        break;
    case OGL_STATE_PGRAM_OP:
        OGLRenderer_EnableAAParallelogramProgram();
        break;
    case OGL_STATE_RESET:
    case OGL_STATE_CHANGE:
        // No-op
        break;
    default:
        // We are starting a primitive operation, so call glBegin() with
        // the given primitive type.
        j2d_glBegin(op);
        break;
    }

    previousOp = op;
}
/**
 * Initializes a framebuffer object based on the given textureID and its
 * width/height.  This method will iterate through all possible depth formats
 * to find one that is supported by the drivers/hardware.  (Since our use of
 * the depth buffer is fairly simplistic, we hope to find a depth format that
 * uses as little VRAM as possible.)  If an appropriate depth buffer is found
 * and all attachments are successful (i.e. the framebuffer object is
 * "complete"), then this method will return JNI_TRUE and will initialize
 * the values of fbobjectID and depthID using the IDs created by this method.
 * Otherwise, this method returns JNI_FALSE.  Note that the caller is only
 * responsible for deleting the allocated fbobject and depth renderbuffer
 * resources if this method returned JNI_TRUE.
 */
jboolean
OGLSD_InitFBObject(GLuint *fbobjectID, GLuint *depthID,
                   GLuint textureID, GLenum textureTarget,
                   jint textureWidth, jint textureHeight)
{
    GLenum depthFormats[] = {
        GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32
    };
    GLuint fboTmpID, depthTmpID;
    jboolean foundDepth = JNI_FALSE;
    int i;

    J2dTraceLn3(J2D_TRACE_INFO, "OGLSD_InitFBObject: w=%d h=%d texid=%d",
                textureWidth, textureHeight, textureID);

    // initialize framebuffer object
    j2d_glGenFramebuffersEXT(1, &fboTmpID);
    j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboTmpID);

    // attach color texture to framebuffer object
    j2d_glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
                                  GL_COLOR_ATTACHMENT0_EXT,
                                  textureTarget, textureID, 0);

    // attempt to create a depth renderbuffer of a particular format; we
    // will start with the smallest size and then work our way up
    for (i = 0; i < 3; i++) {
        GLenum error, status;
        GLenum depthFormat = depthFormats[i];
        int depthSize = 16 + (i * 8);

        // initialize depth renderbuffer
        j2d_glGenRenderbuffersEXT(1, &depthTmpID);
        j2d_glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthTmpID);
        j2d_glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, depthFormat,
                                     textureWidth, textureHeight);

        // creation of depth buffer could potentially fail, so check for error
        error = j2d_glGetError();
        if (error != GL_NO_ERROR) {
            J2dTraceLn2(J2D_TRACE_VERBOSE,
                "OGLSD_InitFBObject: could not create depth buffer: depth=%d error=%x",
                           depthSize, error);
            j2d_glDeleteRenderbuffersEXT(1, &depthTmpID);
            continue;
        }

        // attach depth renderbuffer to framebuffer object
        j2d_glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
                                         GL_DEPTH_ATTACHMENT_EXT,
                                         GL_RENDERBUFFER_EXT, depthTmpID);

        // now check for framebuffer "completeness"
        status = j2d_glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);

        if (status == GL_FRAMEBUFFER_COMPLETE_EXT) {
            // we found a valid format, so break out of the loop
            J2dTraceLn1(J2D_TRACE_VERBOSE,
                        "  framebuffer is complete: depth=%d", depthSize);
            foundDepth = JNI_TRUE;
            break;
        } else {
            // this depth format didn't work, so delete and try another format
            J2dTraceLn2(J2D_TRACE_VERBOSE,
                        "  framebuffer is incomplete: depth=%d status=%x",
                        depthSize, status);
            j2d_glDeleteRenderbuffersEXT(1, &depthTmpID);
        }
    }

    // unbind the texture and framebuffer objects (they will be bound again
    // later as needed)
    j2d_glBindTexture(textureTarget, 0);
    j2d_glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
    j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

    if (!foundDepth) {
        J2dRlsTraceLn(J2D_TRACE_ERROR,
            "OGLSD_InitFBObject: could not find valid depth format");
        j2d_glDeleteFramebuffersEXT(1, &fboTmpID);
        return JNI_FALSE;
    }

    *fbobjectID = fboTmpID;
    *depthID = depthTmpID;

    return JNI_TRUE;
}
/**
 * Initializes an OpenGL texture object.
 *
 * If the isOpaque parameter is JNI_FALSE, then the texture will have a
 * full alpha channel; otherwise, the texture will be opaque (this can
 * help save VRAM when translucency is not needed).
 *
 * If the GL_ARB_texture_non_power_of_two extension is present (texNonPow2
 * is JNI_TRUE), the actual texture is allowed to have non-power-of-two
 * dimensions, and therefore width==textureWidth and height==textureHeight.
 *
 * Failing that, if the GL_ARB_texture_rectangle extension is present
 * (texRect is JNI_TRUE), the actual texture is allowed to have
 * non-power-of-two dimensions, except that instead of using the usual
 * GL_TEXTURE_2D target, we need to use the GL_TEXTURE_RECTANGLE_ARB target.
 * Note that the GL_REPEAT wrapping mode is not allowed with this target,
 * so if that mode is needed (e.g. as is the case in the TexturePaint code)
 * one should pass JNI_FALSE to avoid using this extension.  Also note that
 * when the texture target is GL_TEXTURE_RECTANGLE_ARB, texture coordinates
 * must be specified in the range [0,width] and [0,height] rather than
 * [0,1] as is the case with the usual GL_TEXTURE_2D target (so take care)!
 *
 * Otherwise, the actual texture must have power-of-two dimensions, and
 * therefore the textureWidth and textureHeight will be the next
 * power-of-two greater than (or equal to) the requested width and height.
 */
static jboolean
OGLSD_InitTextureObject(OGLSDOps *oglsdo,
                        jboolean isOpaque,
                        jboolean texNonPow2, jboolean texRect,
                        jint width, jint height)
{
    GLenum texTarget, texProxyTarget;
    GLint format = isOpaque ? GL_RGB : GL_RGBA;
    GLuint texID;
    GLsizei texWidth, texHeight, realWidth, realHeight;
    GLint texMax;

    J2dTraceLn4(J2D_TRACE_INFO,
                "OGLSD_InitTextureObject: w=%d h=%d opq=%d nonpow2=%d",
                width, height, isOpaque, texNonPow2);

    if (oglsdo == NULL) {
        J2dRlsTraceLn(J2D_TRACE_ERROR,
                      "OGLSD_InitTextureObject: ops are null");
        return JNI_FALSE;
    }

    if (texNonPow2) {
        // use non-pow2 dimensions with GL_TEXTURE_2D target
        j2d_glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texMax);
        texWidth = (width <= texMax) ? width : 0;
        texHeight = (height <= texMax) ? height : 0;
        texTarget = GL_TEXTURE_2D;
        texProxyTarget = GL_PROXY_TEXTURE_2D;
    } else if (texRect) {
        // use non-pow2 dimensions with GL_TEXTURE_RECTANGLE_ARB target
        j2d_glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &texMax);
        texWidth = (width <= texMax) ? width : 0;
        texHeight = (height <= texMax) ? height : 0;
        texTarget = GL_TEXTURE_RECTANGLE_ARB;
        texProxyTarget = GL_PROXY_TEXTURE_RECTANGLE_ARB;
    } else {
        // find the appropriate power-of-two dimensions
        j2d_glGetIntegerv(GL_MAX_TEXTURE_SIZE, &texMax);
        texWidth = OGLSD_NextPowerOfTwo(width, texMax);
        texHeight = OGLSD_NextPowerOfTwo(height, texMax);
        texTarget = GL_TEXTURE_2D;
        texProxyTarget = GL_PROXY_TEXTURE_2D;
    }

    J2dTraceLn3(J2D_TRACE_VERBOSE,
                "  desired texture dimensions: w=%d h=%d max=%d",
                texWidth, texHeight, texMax);

    // if either dimension is 0, we cannot allocate a texture with the
    // requested dimensions
    if ((texWidth == 0) || (texHeight == 0)) {
        J2dRlsTraceLn(J2D_TRACE_ERROR,
            "OGLSD_InitTextureObject: texture dimensions too large");
        return JNI_FALSE;
    }

    // now use a proxy to determine whether we can create a texture with
    // the calculated power-of-two dimensions and the given internal format
    j2d_glTexImage2D(texProxyTarget, 0, format,
                     texWidth, texHeight, 0,
                     format, GL_UNSIGNED_BYTE, NULL);
    j2d_glGetTexLevelParameteriv(texProxyTarget, 0,
                                 GL_TEXTURE_WIDTH, &realWidth);
    j2d_glGetTexLevelParameteriv(texProxyTarget, 0,
                                 GL_TEXTURE_HEIGHT, &realHeight);

    // if the requested dimensions and proxy dimensions don't match,
    // we shouldn't attempt to create the texture
    if ((realWidth != texWidth) || (realHeight != texHeight)) {
        J2dRlsTraceLn2(J2D_TRACE_ERROR,
            "OGLSD_InitTextureObject: actual (w=%d h=%d) != requested",
                       realWidth, realHeight);
        return JNI_FALSE;
    }

    // initialize the texture with some dummy data (this allows us to create
    // a texture object once with 2^n dimensions, and then use
    // glTexSubImage2D() to provide further updates)
    j2d_glGenTextures(1, &texID);
    j2d_glBindTexture(texTarget, texID);
    j2d_glTexImage2D(texTarget, 0, format,
                     texWidth, texHeight, 0,
                     format, GL_UNSIGNED_BYTE, NULL);

    oglsdo->isOpaque = isOpaque;
    oglsdo->xOffset = 0;
    oglsdo->yOffset = 0;
    oglsdo->width = width;
    oglsdo->height = height;
    oglsdo->textureID = texID;
    oglsdo->textureWidth = texWidth;
    oglsdo->textureHeight = texHeight;
    oglsdo->textureTarget = texTarget;
    OGLSD_INIT_TEXTURE_FILTER(oglsdo, GL_NEAREST);
    OGLSD_RESET_TEXTURE_WRAP(texTarget);

    J2dTraceLn3(J2D_TRACE_VERBOSE, "  created texture: w=%d h=%d id=%d",
                width, height, texID);

    return JNI_TRUE;
}