/* * Copyright 2004-2009 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. */ #include #include "sun_java2d_opengl_WGLSurfaceData.h" #include "jni.h" #include "jlong.h" #include "jni_util.h" #include "OGLRenderQueue.h" #include "WGLGraphicsConfig.h" #include "WGLSurfaceData.h" /** * The methods in this file implement the native windowing system specific * layer (WGL) for the OpenGL-based Java 2D pipeline. */ extern LockFunc OGLSD_Lock; extern GetRasInfoFunc OGLSD_GetRasInfo; extern UnlockFunc OGLSD_Unlock; extern DisposeFunc OGLSD_Dispose; extern OGLPixelFormat PixelFormats[]; extern void AwtWindow_UpdateWindow(JNIEnv *env, jobject peer, jint w, jint h, HBITMAP hBitmap); extern HBITMAP BitmapUtil_CreateBitmapFromARGBPre(int width, int height, int srcStride, int* imageData); extern void AwtComponent_GetInsets(JNIEnv *env, jobject peer, RECT *insets); extern void OGLSD_SetNativeDimensions(JNIEnv *env, OGLSDOps *oglsdo, jint w, jint h); JNIEXPORT void JNICALL Java_sun_java2d_opengl_WGLSurfaceData_initOps(JNIEnv *env, jobject wglsd, jlong pConfigInfo, jobject peer, jlong hwnd) { OGLSDOps *oglsdo = (OGLSDOps *)SurfaceData_InitOps(env, wglsd, sizeof(OGLSDOps)); WGLSDOps *wglsdo = (WGLSDOps *)malloc(sizeof(WGLSDOps)); J2dTraceLn(J2D_TRACE_INFO, "WGLSurfaceData_initOps"); if (wglsdo == NULL) { JNU_ThrowOutOfMemoryError(env, "creating native wgl ops"); return; } oglsdo->privOps = wglsdo; oglsdo->sdOps.Lock = OGLSD_Lock; oglsdo->sdOps.GetRasInfo = OGLSD_GetRasInfo; oglsdo->sdOps.Unlock = OGLSD_Unlock; oglsdo->sdOps.Dispose = OGLSD_Dispose; oglsdo->drawableType = OGLSD_UNDEFINED; oglsdo->activeBuffer = GL_FRONT; oglsdo->needsInit = JNI_TRUE; if (peer != NULL) { RECT insets; AwtComponent_GetInsets(env, peer, &insets); oglsdo->xOffset = -insets.left; oglsdo->yOffset = -insets.bottom; } else { oglsdo->xOffset = 0; oglsdo->yOffset = 0; } wglsdo->window = (HWND)jlong_to_ptr(hwnd); wglsdo->configInfo = (WGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); if (wglsdo->configInfo == NULL) { free(wglsdo); JNU_ThrowNullPointerException(env, "Config info is null in initOps"); } } /** * This function disposes of any native windowing system resources associated * with this surface. For instance, if the given OGLSDOps is of type * OGLSD_PBUFFER, this method implementation will destroy the actual pbuffer * surface. */ void OGLSD_DestroyOGLSurface(JNIEnv *env, OGLSDOps *oglsdo) { WGLSDOps *wglsdo = (WGLSDOps *)oglsdo->privOps; J2dTraceLn(J2D_TRACE_INFO, "OGLSD_DestroyOGLSurface"); if (oglsdo->drawableType == OGLSD_PBUFFER) { if (wglsdo->pbuffer != 0) { if (wglsdo->pbufferDC != 0) { j2d_wglReleasePbufferDCARB(wglsdo->pbuffer, wglsdo->pbufferDC); wglsdo->pbufferDC = 0; } j2d_wglDestroyPbufferARB(wglsdo->pbuffer); wglsdo->pbuffer = 0; } } } /** * Makes the given context current to its associated "scratch" surface. If * the operation is successful, this method will return JNI_TRUE; otherwise, * returns JNI_FALSE. */ static jboolean WGLSD_MakeCurrentToScratch(JNIEnv *env, OGLContext *oglc) { WGLCtxInfo *ctxInfo; J2dTraceLn(J2D_TRACE_INFO, "WGLSD_MakeCurrentToScratch"); if (oglc == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLSD_MakeCurrentToScratch: context is null"); return JNI_FALSE; } ctxInfo = (WGLCtxInfo *)oglc->ctxInfo; if (!j2d_wglMakeCurrent(ctxInfo->scratchSurfaceDC, ctxInfo->context)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLSD_MakeCurrentToScratch: could not make current"); return JNI_FALSE; } return JNI_TRUE; } /** * Returns a pointer (as a jlong) to the native WGLGraphicsConfigInfo * associated with the given OGLSDOps. This method can be called from * shared code to retrieve the native GraphicsConfig data in a platform- * independent manner. */ jlong OGLSD_GetNativeConfigInfo(OGLSDOps *oglsdo) { WGLSDOps *wglsdo; if (oglsdo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_GetNativeConfigInfo: ops are null"); return 0L; } wglsdo = (WGLSDOps *)oglsdo->privOps; if (wglsdo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_GetNativeConfigInfo: wgl ops are null"); return 0L; } return ptr_to_jlong(wglsdo->configInfo); } /** * Makes the given GraphicsConfig's context current to its associated * "scratch" surface. 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 given GraphicsConfig. */ OGLContext * OGLSD_SetScratchSurface(JNIEnv *env, jlong pConfigInfo) { WGLGraphicsConfigInfo *wglInfo = (WGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); OGLContext *oglc; J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SetScratchContext"); if (wglInfo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_SetScratchContext: wgl config info is null"); return NULL; } oglc = wglInfo->context; if (!WGLSD_MakeCurrentToScratch(env, oglc)) { return NULL; } if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) { // the GL_EXT_framebuffer_object extension is present, so this call // will ensure that we are bound to the scratch pbuffer (and not // some other framebuffer object) j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } return oglc; } /** * 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; } /** * This function initializes a native window surface and caches the window * bounds in the given OGLSDOps. Returns JNI_TRUE if the operation was * successful; JNI_FALSE otherwise. */ jboolean OGLSD_InitOGLWindow(JNIEnv *env, OGLSDOps *oglsdo) { PIXELFORMATDESCRIPTOR pfd; WGLSDOps *wglsdo; WGLGraphicsConfigInfo *wglInfo; HWND window; RECT wbounds; HDC hdc; J2dTraceLn(J2D_TRACE_INFO, "OGLSD_InitOGLWindow"); if (oglsdo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: ops are null"); return JNI_FALSE; } wglsdo = (WGLSDOps *)oglsdo->privOps; if (wglsdo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: wgl ops are null"); return JNI_FALSE; } wglInfo = wglsdo->configInfo; if (wglInfo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: graphics config info is null"); return JNI_FALSE; } window = wglsdo->window; if (!IsWindow(window)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: disposed component"); return JNI_FALSE; } GetWindowRect(window, &wbounds); hdc = GetDC(window); if (hdc == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: invalid hdc"); return JNI_FALSE; } if (!SetPixelFormat(hdc, wglInfo->pixfmt, &pfd)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_InitOGLWindow: error setting pixel format"); ReleaseDC(window, hdc); return JNI_FALSE; } ReleaseDC(window, hdc); oglsdo->drawableType = OGLSD_WINDOW; oglsdo->isOpaque = JNI_TRUE; oglsdo->width = wbounds.right - wbounds.left; oglsdo->height = wbounds.bottom - wbounds.top; wglsdo->pbufferDC = 0; J2dTraceLn2(J2D_TRACE_VERBOSE, " created window: w=%d h=%d", oglsdo->width, oglsdo->height); return JNI_TRUE; } JNIEXPORT jboolean JNICALL Java_sun_java2d_opengl_WGLSurfaceData_initPbuffer (JNIEnv *env, jobject wglsd, jlong pData, jlong pConfigInfo, jboolean isOpaque, jint width, jint height) { int attrKeys[] = { WGL_MAX_PBUFFER_WIDTH_ARB, WGL_MAX_PBUFFER_HEIGHT_ARB, }; int attrVals[2]; int pbAttrList[] = { 0 }; OGLSDOps *oglsdo = (OGLSDOps *)jlong_to_ptr(pData); WGLGraphicsConfigInfo *wglInfo = (WGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); WGLSDOps *wglsdo; HWND hwnd; HDC hdc, pbufferDC; HPBUFFERARB pbuffer; int maxWidth, maxHeight; int actualWidth, actualHeight; J2dTraceLn3(J2D_TRACE_INFO, "WGLSurfaceData_initPbuffer: w=%d h=%d opq=%d", width, height, isOpaque); if (oglsdo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLSurfaceData_initPbuffer: ops are null"); return JNI_FALSE; } wglsdo = (WGLSDOps *)oglsdo->privOps; if (wglsdo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLSurfaceData_initPbuffer: wgl ops are null"); return JNI_FALSE; } if (wglInfo == NULL) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLSurfaceData_initPbuffer: wgl config info is null"); return JNI_FALSE; } // create a scratch window hwnd = WGLGC_CreateScratchWindow(wglInfo->screen); if (hwnd == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLSurfaceData_initPbuffer: could not create scratch window"); return JNI_FALSE; } // get the HDC for the scratch window hdc = GetDC(hwnd); if (hdc == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLSurfaceData_initPbuffer: could not get dc for scratch window"); DestroyWindow(hwnd); return JNI_FALSE; } // get the maximum allowable pbuffer dimensions j2d_wglGetPixelFormatAttribivARB(hdc, wglInfo->pixfmt, 0, 2, attrKeys, attrVals); maxWidth = attrVals[0]; maxHeight = attrVals[1]; J2dTraceLn4(J2D_TRACE_VERBOSE, " desired pbuffer dimensions: w=%d h=%d maxw=%d maxh=%d", width, height, maxWidth, maxHeight); // if either dimension is 0 or larger than the maximum, we cannot // allocate a pbuffer with the requested dimensions if (width == 0 || width > maxWidth || height == 0 || height > maxHeight) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLSurfaceData_initPbuffer: invalid dimensions"); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); return JNI_FALSE; } pbuffer = j2d_wglCreatePbufferARB(hdc, wglInfo->pixfmt, width, height, pbAttrList); ReleaseDC(hwnd, hdc); DestroyWindow(hwnd); if (pbuffer == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLSurfaceData_initPbuffer: could not create wgl pbuffer"); return JNI_FALSE; } // note that we get the DC for the pbuffer at creation time, and then // release the DC when the pbuffer is disposed; the WGL_ARB_pbuffer // spec is vague about such things, but from past experience we know // this approach to be more robust than, for example, doing a // Get/ReleasePbufferDC() everytime we make a context current pbufferDC = j2d_wglGetPbufferDCARB(pbuffer); if (pbufferDC == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "WGLSurfaceData_initPbuffer: could not get dc for pbuffer"); j2d_wglDestroyPbufferARB(pbuffer); return JNI_FALSE; } // make sure the actual dimensions match those that we requested j2d_wglQueryPbufferARB(pbuffer, WGL_PBUFFER_WIDTH_ARB, &actualWidth); j2d_wglQueryPbufferARB(pbuffer, WGL_PBUFFER_HEIGHT_ARB, &actualHeight); if (width != actualWidth || height != actualHeight) { J2dRlsTraceLn2(J2D_TRACE_ERROR, "WGLSurfaceData_initPbuffer: actual (w=%d h=%d) != requested", actualWidth, actualHeight); j2d_wglReleasePbufferDCARB(pbuffer, pbufferDC); j2d_wglDestroyPbufferARB(pbuffer); return JNI_FALSE; } oglsdo->drawableType = OGLSD_PBUFFER; oglsdo->isOpaque = isOpaque; oglsdo->width = width; oglsdo->height = height; wglsdo->pbuffer = pbuffer; wglsdo->pbufferDC = pbufferDC; OGLSD_SetNativeDimensions(env, oglsdo, width, height); return JNI_TRUE; } void OGLSD_SwapBuffers(JNIEnv *env, jlong pPeerData) { HWND window; HDC hdc; J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SwapBuffers"); window = AwtComponent_GetHWnd(env, pPeerData); if (!IsWindow(window)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_SwapBuffers: disposed component"); return; } hdc = GetDC(window); if (hdc == 0) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_SwapBuffers: invalid hdc"); return; } if (!SwapBuffers(hdc)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_SwapBuffers: error in SwapBuffers"); } if (!ReleaseDC(window, hdc)) { J2dRlsTraceLn(J2D_TRACE_ERROR, "OGLSD_SwapBuffers: error while releasing dc"); } } /* * Class: sun_java2d_opengl_WGLSurfaceData * Method: updateWindowAccelImpl * Signature: (JJII)Z */ JNIEXPORT jboolean JNICALL Java_sun_java2d_opengl_WGLSurfaceData_updateWindowAccelImpl (JNIEnv *env, jclass clazz, jlong pData, jobject peer, jint w, jint h) { OGLSDOps *oglsdo = (OGLSDOps *)jlong_to_ptr(pData); OGLPixelFormat pf = PixelFormats[0/*PF_INT_ARGB_PRE*/]; HBITMAP hBitmap = NULL; void *pDst; jint srcx, srcy, dstx, dsty, width, height; jint pixelStride = 4; jint scanStride = pixelStride * w; J2dTraceLn(J2D_TRACE_INFO, "WGLSurfaceData_updateWindowAccelImpl"); if (w <= 0 || h <= 0) { return JNI_TRUE; } if (oglsdo == NULL) { return JNI_FALSE; } RESET_PREVIOUS_OP(); width = w; height = h; srcx = srcy = dstx = dsty = 0; pDst = malloc(height * scanStride); if (pDst == NULL) { return JNI_FALSE; } ZeroMemory(pDst, height * scanStride); // the code below is mostly copied from OGLBlitLoops_SurfaceToSwBlit j2d_glPixelStorei(GL_PACK_SKIP_PIXELS, dstx); j2d_glPixelStorei(GL_PACK_ROW_LENGTH, scanStride / pixelStride); j2d_glPixelStorei(GL_PACK_ALIGNMENT, pf.alignment); // this accounts for lower-left origin of the source region srcx = oglsdo->xOffset + srcx; srcy = oglsdo->yOffset + oglsdo->height - (srcy + 1); // we must read one scanline at a time because there is no way // to read starting at the top-left corner of the source region while (height > 0) { j2d_glPixelStorei(GL_PACK_SKIP_ROWS, dsty); j2d_glReadPixels(srcx, srcy, width, 1, pf.format, pf.type, pDst); srcy--; dsty++; height--; } j2d_glPixelStorei(GL_PACK_SKIP_PIXELS, 0); j2d_glPixelStorei(GL_PACK_SKIP_ROWS, 0); j2d_glPixelStorei(GL_PACK_ROW_LENGTH, 0); j2d_glPixelStorei(GL_PACK_ALIGNMENT, 4); // the pixels read from the surface are already premultiplied hBitmap = BitmapUtil_CreateBitmapFromARGBPre(w, h, scanStride, (int*)pDst); free(pDst); if (hBitmap == NULL) { return JNI_FALSE; } AwtWindow_UpdateWindow(env, peer, w, h, hBitmap); // hBitmap is released in UpdateWindow return JNI_TRUE; }