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 }