/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
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); }
/** * 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; }
/** * 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; }
/** * 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; }