1 /* 2 * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include <stdlib.h> 27 28 #include "sun_java2d_opengl_WGLSurfaceData.h" 29 30 #include "jni.h" 31 #include "jlong.h" 32 #include "jni_util.h" 33 #include "sizecalc.h" 34 #include "OGLRenderQueue.h" 35 #include "WGLGraphicsConfig.h" 36 #include "WGLSurfaceData.h" 37 38 /** 39 * The methods in this file implement the native windowing system specific 40 * layer (WGL) for the OpenGL-based Java 2D pipeline. 41 */ 42 43 extern LockFunc OGLSD_Lock; 44 extern GetRasInfoFunc OGLSD_GetRasInfo; 45 extern UnlockFunc OGLSD_Unlock; 46 extern DisposeFunc OGLSD_Dispose; 47 48 extern OGLPixelFormat PixelFormats[]; 49 extern void AwtWindow_UpdateWindow(JNIEnv *env, jobject peer, 50 jint w, jint h, HBITMAP hBitmap); 51 extern HBITMAP BitmapUtil_CreateBitmapFromARGBPre(int width, int height, 52 int srcStride, 53 int* imageData); 54 extern void AwtComponent_GetInsets(JNIEnv *env, jobject peer, RECT *insets); 55 56 extern void 57 OGLSD_SetNativeDimensions(JNIEnv *env, OGLSDOps *oglsdo, jint w, jint h); 58 59 JNIEXPORT void JNICALL 60 Java_sun_java2d_opengl_WGLSurfaceData_initOps(JNIEnv *env, jobject wglsd, 61 jobject gc, jlong pConfigInfo, 62 jobject peer, jlong hwnd) 63 { 64 gc = (*env)->NewGlobalRef(env, gc); 65 if (gc == NULL) { 66 JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed."); 67 return; 68 } 69 70 OGLSDOps *oglsdo = (OGLSDOps *)SurfaceData_InitOps(env, wglsd, 71 sizeof(OGLSDOps)); 72 if (oglsdo == NULL) { 73 (*env)->DeleteGlobalRef(env, gc); 74 JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed."); 75 return; 76 } 77 // later the graphicsConfig will be used for deallocation of oglsdo 78 oglsdo->graphicsConfig = gc; 79 80 WGLSDOps *wglsdo = (WGLSDOps *)malloc(sizeof(WGLSDOps)); 81 82 J2dTraceLn(J2D_TRACE_INFO, "WGLSurfaceData_initOps"); 83 84 if (wglsdo == NULL) { 85 JNU_ThrowOutOfMemoryError(env, "creating native wgl ops"); 86 return; 87 } 88 if (oglsdo == NULL) { 89 free(wglsdo); 90 JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed."); 91 return; 92 } 93 94 oglsdo->privOps = wglsdo; 95 96 oglsdo->sdOps.Lock = OGLSD_Lock; 97 oglsdo->sdOps.GetRasInfo = OGLSD_GetRasInfo; 98 oglsdo->sdOps.Unlock = OGLSD_Unlock; 99 oglsdo->sdOps.Dispose = OGLSD_Dispose; 100 101 oglsdo->drawableType = OGLSD_UNDEFINED; 102 oglsdo->activeBuffer = GL_FRONT; 103 oglsdo->needsInit = JNI_TRUE; 104 if (peer != NULL) { 105 RECT insets; 106 AwtComponent_GetInsets(env, peer, &insets); 107 oglsdo->xOffset = -insets.left; 108 oglsdo->yOffset = -insets.bottom; 109 } else { 110 oglsdo->xOffset = 0; 111 oglsdo->yOffset = 0; 112 } 113 114 wglsdo->window = (HWND)jlong_to_ptr(hwnd); 115 wglsdo->configInfo = (WGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); 116 if (wglsdo->configInfo == NULL) { 117 free(wglsdo); 118 JNU_ThrowNullPointerException(env, "Config info is null in initOps"); 119 } 120 } 121 122 /** 123 * This function disposes of any native windowing system resources associated 124 * with this surface. 125 */ 126 void 127 OGLSD_DestroyOGLSurface(JNIEnv *env, OGLSDOps *oglsdo) 128 { 129 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_DestroyOGLSurface"); 130 // Window is free'd later by AWT code... 131 } 132 133 /** 134 * Makes the given context current to its associated "scratch" surface. If 135 * the operation is successful, this method will return JNI_TRUE; otherwise, 136 * returns JNI_FALSE. 137 */ 138 static jboolean 139 WGLSD_MakeCurrentToScratch(JNIEnv *env, OGLContext *oglc) 140 { 141 WGLCtxInfo *ctxInfo; 142 143 J2dTraceLn(J2D_TRACE_INFO, "WGLSD_MakeCurrentToScratch"); 144 145 if (oglc == NULL) { 146 J2dRlsTraceLn(J2D_TRACE_ERROR, 147 "WGLSD_MakeCurrentToScratch: context is null"); 148 return JNI_FALSE; 149 } 150 151 ctxInfo = (WGLCtxInfo *)oglc->ctxInfo; 152 if (!j2d_wglMakeCurrent(ctxInfo->scratchSurfaceDC, ctxInfo->context)) { 153 J2dRlsTraceLn(J2D_TRACE_ERROR, 154 "WGLSD_MakeCurrentToScratch: could not make current"); 155 return JNI_FALSE; 156 } 157 158 return JNI_TRUE; 159 } 160 161 /** 162 * Makes the given GraphicsConfig's context current to its associated 163 * "scratch" surface. If there is a problem making the context current, 164 * this method will return NULL; otherwise, returns a pointer to the 165 * OGLContext that is associated with the given GraphicsConfig. 166 */ 167 OGLContext * 168 OGLSD_SetScratchSurface(JNIEnv *env, jlong pConfigInfo) 169 { 170 WGLGraphicsConfigInfo *wglInfo = 171 (WGLGraphicsConfigInfo *)jlong_to_ptr(pConfigInfo); 172 OGLContext *oglc; 173 174 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SetScratchContext"); 175 176 if (wglInfo == NULL) { 177 J2dRlsTraceLn(J2D_TRACE_ERROR, 178 "OGLSD_SetScratchContext: wgl config info is null"); 179 return NULL; 180 } 181 182 oglc = wglInfo->context; 183 if (!WGLSD_MakeCurrentToScratch(env, oglc)) { 184 return NULL; 185 } 186 187 if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) { 188 // the GL_EXT_framebuffer_object extension is present, so this call 189 // will ensure that we are bound to the scratch pbuffer (and not 190 // some other framebuffer object) 191 j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 192 } 193 194 return oglc; 195 } 196 197 /** 198 * Makes a context current to the given source and destination 199 * surfaces. If there is a problem making the context current, this method 200 * will return NULL; otherwise, returns a pointer to the OGLContext that is 201 * associated with the destination surface. 202 */ 203 OGLContext * 204 OGLSD_MakeOGLContextCurrent(JNIEnv *env, OGLSDOps *srcOps, OGLSDOps *dstOps) 205 { 206 WGLSDOps *srcWGLOps = (WGLSDOps *)srcOps->privOps; 207 WGLSDOps *dstWGLOps = (WGLSDOps *)dstOps->privOps; 208 OGLContext *oglc; 209 WGLCtxInfo *ctxinfo; 210 HDC srcHDC, dstHDC; 211 BOOL success; 212 213 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_MakeOGLContextCurrent"); 214 215 J2dTraceLn4(J2D_TRACE_VERBOSE, " src: %d %p dst: %d %p", 216 srcOps->drawableType, srcOps, 217 dstOps->drawableType, dstOps); 218 219 oglc = dstWGLOps->configInfo->context; 220 if (oglc == NULL) { 221 J2dRlsTraceLn(J2D_TRACE_ERROR, 222 "OGLSD_MakeOGLContextCurrent: context is null"); 223 return NULL; 224 } 225 226 if (dstOps->drawableType == OGLSD_FBOBJECT) { 227 OGLContext *currentContext = OGLRenderQueue_GetCurrentContext(); 228 229 // first make sure we have a current context (if the context isn't 230 // already current to some drawable, we will make it current to 231 // its scratch surface) 232 if (oglc != currentContext) { 233 if (!WGLSD_MakeCurrentToScratch(env, oglc)) { 234 return NULL; 235 } 236 } 237 238 // now bind to the fbobject associated with the destination surface; 239 // this means that all rendering will go into the fbobject destination 240 // (note that we unbind the currently bound texture first; this is 241 // recommended procedure when binding an fbobject) 242 j2d_glBindTexture(dstOps->textureTarget, 0); 243 j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, dstOps->fbobjectID); 244 245 return oglc; 246 } 247 248 ctxinfo = (WGLCtxInfo *)oglc->ctxInfo; 249 250 // get the hdc for the destination surface 251 dstHDC = GetDC(dstWGLOps->window); 252 253 // get the hdc for the source surface 254 // the source will always be equal to the destination in this case 255 srcHDC = dstHDC; 256 257 // REMIND: in theory we should be able to use wglMakeContextCurrentARB() 258 // even when the src/dst surfaces are the same, but this causes problems 259 // on ATI's drivers (see 6525997); for now we will only use it when the 260 // surfaces are different, otherwise we will use the old 261 // wglMakeCurrent() approach... 262 if (srcHDC != dstHDC) { 263 // use WGL_ARB_make_current_read extension to make context current 264 success = 265 j2d_wglMakeContextCurrentARB(dstHDC, srcHDC, ctxinfo->context); 266 } else { 267 // use the old approach for making current to the destination 268 success = j2d_wglMakeCurrent(dstHDC, ctxinfo->context); 269 } 270 if (!success) { 271 J2dRlsTraceLn(J2D_TRACE_ERROR, 272 "OGLSD_MakeOGLContextCurrent: could not make current"); 273 ReleaseDC(dstWGLOps->window, dstHDC); 274 return NULL; 275 } 276 277 if (OGLC_IS_CAP_PRESENT(oglc, CAPS_EXT_FBOBJECT)) { 278 // the GL_EXT_framebuffer_object extension is present, so we 279 // must bind to the default (windowing system provided) 280 // framebuffer 281 j2d_glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); 282 } 283 284 ReleaseDC(dstWGLOps->window, dstHDC); 285 286 return oglc; 287 } 288 289 /** 290 * This function initializes a native window surface and caches the window 291 * bounds in the given OGLSDOps. Returns JNI_TRUE if the operation was 292 * successful; JNI_FALSE otherwise. 293 */ 294 jboolean 295 OGLSD_InitOGLWindow(JNIEnv *env, OGLSDOps *oglsdo) 296 { 297 PIXELFORMATDESCRIPTOR pfd; 298 WGLSDOps *wglsdo; 299 WGLGraphicsConfigInfo *wglInfo; 300 HWND window; 301 RECT wbounds; 302 HDC hdc; 303 304 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_InitOGLWindow"); 305 306 if (oglsdo == NULL) { 307 J2dRlsTraceLn(J2D_TRACE_ERROR, 308 "OGLSD_InitOGLWindow: ops are null"); 309 return JNI_FALSE; 310 } 311 312 wglsdo = (WGLSDOps *)oglsdo->privOps; 313 if (wglsdo == NULL) { 314 J2dRlsTraceLn(J2D_TRACE_ERROR, 315 "OGLSD_InitOGLWindow: wgl ops are null"); 316 return JNI_FALSE; 317 } 318 319 wglInfo = wglsdo->configInfo; 320 if (wglInfo == NULL) { 321 J2dRlsTraceLn(J2D_TRACE_ERROR, 322 "OGLSD_InitOGLWindow: graphics config info is null"); 323 return JNI_FALSE; 324 } 325 326 window = wglsdo->window; 327 if (!IsWindow(window)) { 328 J2dRlsTraceLn(J2D_TRACE_ERROR, 329 "OGLSD_InitOGLWindow: disposed component"); 330 return JNI_FALSE; 331 } 332 333 GetWindowRect(window, &wbounds); 334 335 hdc = GetDC(window); 336 if (hdc == 0) { 337 J2dRlsTraceLn(J2D_TRACE_ERROR, 338 "OGLSD_InitOGLWindow: invalid hdc"); 339 return JNI_FALSE; 340 } 341 342 if (!SetPixelFormat(hdc, wglInfo->pixfmt, &pfd)) { 343 J2dRlsTraceLn(J2D_TRACE_ERROR, 344 "OGLSD_InitOGLWindow: error setting pixel format"); 345 ReleaseDC(window, hdc); 346 return JNI_FALSE; 347 } 348 349 ReleaseDC(window, hdc); 350 351 oglsdo->drawableType = OGLSD_WINDOW; 352 oglsdo->isOpaque = JNI_TRUE; 353 oglsdo->width = wbounds.right - wbounds.left; 354 oglsdo->height = wbounds.bottom - wbounds.top; 355 wglsdo->pbufferDC = 0; 356 357 J2dTraceLn2(J2D_TRACE_VERBOSE, " created window: w=%d h=%d", 358 oglsdo->width, oglsdo->height); 359 360 return JNI_TRUE; 361 } 362 363 void 364 OGLSD_SwapBuffers(JNIEnv *env, jlong pPeerData) 365 { 366 HWND window; 367 HDC hdc; 368 369 J2dTraceLn(J2D_TRACE_INFO, "OGLSD_SwapBuffers"); 370 371 window = AwtComponent_GetHWnd(env, pPeerData); 372 if (!IsWindow(window)) { 373 J2dRlsTraceLn(J2D_TRACE_ERROR, 374 "OGLSD_SwapBuffers: disposed component"); 375 return; 376 } 377 378 hdc = GetDC(window); 379 if (hdc == 0) { 380 J2dRlsTraceLn(J2D_TRACE_ERROR, 381 "OGLSD_SwapBuffers: invalid hdc"); 382 return; 383 } 384 385 if (!SwapBuffers(hdc)) { 386 J2dRlsTraceLn(J2D_TRACE_ERROR, 387 "OGLSD_SwapBuffers: error in SwapBuffers"); 388 } 389 390 if (!ReleaseDC(window, hdc)) { 391 J2dRlsTraceLn(J2D_TRACE_ERROR, 392 "OGLSD_SwapBuffers: error while releasing dc"); 393 } 394 } 395 396 // needed by Mac OS X port, no-op on other platforms 397 void 398 OGLSD_Flush(JNIEnv *env) 399 { 400 } 401 402 /* 403 * Class: sun_java2d_opengl_WGLSurfaceData 404 * Method: updateWindowAccelImpl 405 * Signature: (JJII)Z 406 */ 407 JNIEXPORT jboolean JNICALL 408 Java_sun_java2d_opengl_WGLSurfaceData_updateWindowAccelImpl 409 (JNIEnv *env, jclass clazz, jlong pData, jobject peer, jint w, jint h) 410 { 411 OGLSDOps *oglsdo = (OGLSDOps *)jlong_to_ptr(pData); 412 OGLPixelFormat pf = PixelFormats[0/*PF_INT_ARGB_PRE*/]; 413 HBITMAP hBitmap = NULL; 414 void *pDst; 415 jint srcx, srcy, dstx, dsty, width, height; 416 jint pixelStride = 4; 417 jint scanStride = pixelStride * w; 418 419 J2dTraceLn(J2D_TRACE_INFO, "WGLSurfaceData_updateWindowAccelImpl"); 420 421 if (w <= 0 || h <= 0) { 422 return JNI_TRUE; 423 } 424 if (oglsdo == NULL) { 425 return JNI_FALSE; 426 } 427 RESET_PREVIOUS_OP(); 428 429 width = w; 430 height = h; 431 srcx = srcy = dstx = dsty = 0; 432 433 pDst = SAFE_SIZE_ARRAY_ALLOC(malloc, height, scanStride); 434 if (pDst == NULL) { 435 return JNI_FALSE; 436 } 437 ZeroMemory(pDst, height * scanStride); 438 439 // the code below is mostly copied from OGLBlitLoops_SurfaceToSwBlit 440 441 j2d_glPixelStorei(GL_PACK_SKIP_PIXELS, dstx); 442 j2d_glPixelStorei(GL_PACK_ROW_LENGTH, scanStride / pixelStride); 443 j2d_glPixelStorei(GL_PACK_ALIGNMENT, pf.alignment); 444 445 // this accounts for lower-left origin of the source region 446 srcx = oglsdo->xOffset + srcx; 447 srcy = oglsdo->yOffset + oglsdo->height - (srcy + 1); 448 // we must read one scanline at a time because there is no way 449 // to read starting at the top-left corner of the source region 450 while (height > 0) { 451 j2d_glPixelStorei(GL_PACK_SKIP_ROWS, dsty); 452 j2d_glReadPixels(srcx, srcy, width, 1, 453 pf.format, pf.type, pDst); 454 srcy--; 455 dsty++; 456 height--; 457 } 458 459 j2d_glPixelStorei(GL_PACK_SKIP_PIXELS, 0); 460 j2d_glPixelStorei(GL_PACK_SKIP_ROWS, 0); 461 j2d_glPixelStorei(GL_PACK_ROW_LENGTH, 0); 462 j2d_glPixelStorei(GL_PACK_ALIGNMENT, 4); 463 464 // the pixels read from the surface are already premultiplied 465 hBitmap = BitmapUtil_CreateBitmapFromARGBPre(w, h, scanStride, 466 (int*)pDst); 467 free(pDst); 468 469 if (hBitmap == NULL) { 470 return JNI_FALSE; 471 } 472 473 AwtWindow_UpdateWindow(env, peer, w, h, hBitmap); 474 475 // hBitmap is released in UpdateWindow 476 477 return JNI_TRUE; 478 }