/* * Copyright (c) 1999, 2015, Oracle and/or its affiliates. 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ #include "sun_java2d_windows_GDIWindowSurfaceData.h" #include "GDIWindowSurfaceData.h" #include "GraphicsPrimitiveMgr.h" #include "Region.h" #include "Disposer.h" #include "WindowsFlags.h" #include "awt_Component.h" #include "awt_Palette.h" #include "awt_Win32GraphicsDevice.h" #include "gdefs.h" #include "Trace.h" #include "Devices.h" #include "jni_util.h" static LockFunc GDIWinSD_Lock; static GetRasInfoFunc GDIWinSD_GetRasInfo; static UnlockFunc GDIWinSD_Unlock; static DisposeFunc GDIWinSD_Dispose; static SetupFunc GDIWinSD_Setup; static GetDCFunc GDIWinSD_GetDC; static ReleaseDCFunc GDIWinSD_ReleaseDC; static InvalidateSDFunc GDIWinSD_InvalidateSD; static HBRUSH nullbrush; static HPEN nullpen; static jclass xorCompClass; static jboolean beingShutdown = JNI_FALSE; static volatile LONG timeStamp = 0; extern CriticalSection windowMoveLock; extern "C" { GeneralDisposeFunc DisposeThreadGraphicsInfo; jobject JNI_GetCurrentThread(JNIEnv *env); int threadInfoIndex = TLS_OUT_OF_INDEXES; static jclass threadClass = NULL; static jmethodID currentThreadMethodID = NULL; void SetupThreadGraphicsInfo(JNIEnv *env, GDIWinSDOps *wsdo) { J2dTraceLn(J2D_TRACE_INFO, "SetupThreadGraphicsInfo"); // REMIND: handle error when creation fails ThreadGraphicsInfo *info = (ThreadGraphicsInfo*)TlsGetValue(threadInfoIndex); if (info == NULL) { info = new ThreadGraphicsInfo; ZeroMemory(info, sizeof(ThreadGraphicsInfo)); TlsSetValue(threadInfoIndex, (LPVOID)info); J2dTraceLn2(J2D_TRACE_VERBOSE, " current batch limit for thread 0x%x is %d", GetCurrentThreadId(), ::GdiGetBatchLimit()); J2dTraceLn(J2D_TRACE_VERBOSE, " setting to the limit to 1"); // Fix for bug 4374079 ::GdiSetBatchLimit(1); Disposer_AddRecord(env, JNI_GetCurrentThread(env), DisposeThreadGraphicsInfo, ptr_to_jlong(info)); } HDC oldhDC = info->hDC; // the hDC is NULL for offscreen surfaces - we don't store it // in TLS as it must be created new every time. if( ((oldhDC == NULL) && wsdo->window != NULL) || (info->wsdo != wsdo) || (info->wsdoTimeStamp != wsdo->timeStamp) ) { // Init graphics state, either because this is our first time // using it in this thread or because this thread is now // dealing with a different window than it was last time. //check extra condition: //(info->wsdoTimeStamp != wsdo->timeStamp). //Checking memory addresses (info->wsdo != wsdo) will not detect //that wsdo points to a newly allocated structure in case //that structure just got allocated at a "recycled" memory location //which previously was pointed by info->wsdo //see bug# 6859086 // Release cached DC. We use deferred DC releasing mechanism because // the DC is associated with cached wsdo and component peer, // which may've been disposed by this time, and we have // no means of checking against it. if (oldhDC != NULL) { MoveDCToPassiveList(oldhDC, info->hWnd); info->hDC = NULL; info->hWnd = NULL; } if (wsdo->window != NULL){ HDC hDC; // This is a window surface // First, init the HDC object AwtComponent *comp = GDIWindowSurfaceData_GetComp(env, wsdo); if (comp == NULL) { return; } hDC = comp->GetDCFromComponent(); if (hDC != NULL && wsdo->device != NULL) { ::SelectObject(hDC, nullbrush); ::SelectObject(hDC, nullpen); ::SelectClipRgn(hDC, (HRGN) NULL); ::SetROP2(hDC, R2_COPYPEN); wsdo->device->SelectPalette(hDC); // Note that on NT4 we don't need to do a realize here: the // palette-sharing takes care of color issues for us. But // on win98 if we don't realize a DC's palette, that // palette does not appear to have correct access to the // logical->system mapping. wsdo->device->RealizePalette(hDC); // Second, init the rest of the graphics state ::GetClientRect(wsdo->window, &info->bounds); // Make window-relative from client-relative ::OffsetRect(&info->bounds, wsdo->insets.left, wsdo->insets.top); //Likewise, translate GDI calls from client-relative to window-relative ::OffsetViewportOrgEx(hDC, -wsdo->insets.left, -wsdo->insets.top, NULL); } // Finally, set these new values in the info for this thread info->hDC = hDC; info->hWnd = wsdo->window; } // cached brush and pen are not associated with any DC, and can be // reused, but have to set type to 0 to indicate that no pen/brush // were set to the new hdc info->type = 0; if (info->clip != NULL) { env->DeleteWeakGlobalRef(info->clip); } info->clip = NULL; if (info->comp != NULL) { env->DeleteWeakGlobalRef(info->comp); } info->comp = NULL; info->xorcolor = 0; info->patrop = PATCOPY; //store the address and time stamp of newly allocated GDIWinSDOps structure info->wsdo = wsdo; info->wsdoTimeStamp = wsdo->timeStamp; } } /** * Releases native data stored in Thread local storage. * Called by the Disposer when the associated thread dies. */ void DisposeThreadGraphicsInfo(JNIEnv *env, jlong tgi) { J2dTraceLn(J2D_TRACE_INFO, "DisposeThreadGraphicsInfo"); ThreadGraphicsInfo *info = (ThreadGraphicsInfo*)jlong_to_ptr(tgi); if (info != NULL) { if (info->hDC != NULL) { // move the DC from the active dcs list to // the passive dc list to be released later MoveDCToPassiveList(info->hDC, info->hWnd); } if (info->clip != NULL) { env->DeleteWeakGlobalRef(info->clip); } if (info->comp != NULL) { env->DeleteWeakGlobalRef(info->comp); } if (info->brush != NULL) { info->brush->Release(); } if (info->pen != NULL) { info->pen->Release(); } delete info; } } /** * Returns current Thread object. */ jobject JNI_GetCurrentThread(JNIEnv *env) { return env->CallStaticObjectMethod(threadClass, currentThreadMethodID); } /* JNI_GetCurrentThread() */ /** * Return the data associated with this thread. * NOTE: This function assumes that the SetupThreadGraphicsInfo() * function has already been called for this situation (thread, * window, etc.), so we can assume that the thread info contains * a valid hDC. This should usually be the case since GDIWinSD_Setup * is called as part of the GetOps() process. */ ThreadGraphicsInfo *GetThreadGraphicsInfo(JNIEnv *env, GDIWinSDOps *wsdo) { return (ThreadGraphicsInfo*)TlsGetValue(threadInfoIndex); } __inline HDC GetThreadDC(JNIEnv *env, GDIWinSDOps *wsdo) { ThreadGraphicsInfo *info = (ThreadGraphicsInfo *)GetThreadGraphicsInfo(env, wsdo); if (!info) { return (HDC) NULL; } return info->hDC; } } // extern "C" /** * This source file contains support code for loops using the * SurfaceData interface to talk to a Win32 drawable from native * code. */ static BOOL GDIWinSD_CheckMonitorArea(GDIWinSDOps *wsdo, SurfaceDataBounds *bounds, HDC hDC) { HWND hW = wsdo->window; BOOL retCode = TRUE; J2dTraceLn(J2D_TRACE_INFO, "GDIWinSD_CheckMonitorArea"); int numScreens; { Devices::InstanceAccess devices; numScreens = devices->GetNumDevices(); } if( numScreens > 1 ) { LPMONITORINFO miInfo; RECT rSect ={0,0,0,0}; RECT rView ={bounds->x1, bounds->y1, bounds->x2, bounds->y2}; retCode = FALSE; miInfo = wsdo->device->GetMonitorInfo(); POINT ptOrig = {0, 0}; ::ClientToScreen(hW, &ptOrig); ::OffsetRect(&rView, (ptOrig.x), (ptOrig.y)); ::IntersectRect(&rSect,&rView,&(miInfo->rcMonitor)); if( FALSE == ::IsRectEmpty(&rSect) ) { if( TRUE == ::EqualRect(&rSect,&rView) ) { retCode = TRUE; } } } return retCode; } extern "C" { void initThreadInfoIndex() { if (threadInfoIndex == TLS_OUT_OF_INDEXES) { threadInfoIndex = TlsAlloc(); } } /** * Utility function to make sure that native and java-level * surface depths are matched. They can be mismatched when display-depths * change, either between the creation of the Java surfaceData structure * and the native ddraw surface, or later when a surface is automatically * adjusted to be the new display depth (even if it was created in a different * depth to begin with) */ BOOL SurfaceDepthsCompatible(int javaDepth, int nativeDepth) { if (nativeDepth != javaDepth) { switch (nativeDepth) { case 0: // Error condition: something is wrong with the surface case 8: case 24: // Java and native surface depths should match exactly for // these cases return FALSE; break; case 16: // Java surfaceData should be 15 or 16 bits if (javaDepth < 15 || javaDepth > 16) { return FALSE; } break; case 32: // Could have this native depth for either 24- or 32-bit // Java surfaceData if (javaDepth != 24 && javaDepth != 32) { return FALSE; } break; default: // should not get here, but if we do something is odd, so // just register a failure return FALSE; } } return TRUE; } /* * Class: sun_java2d_windows_GDIWindowSurfaceData * Method: initIDs * Signature: ()V */ JNIEXPORT void JNICALL Java_sun_java2d_windows_GDIWindowSurfaceData_initIDs(JNIEnv *env, jclass wsd, jclass XORComp) { jclass tc; J2dTraceLn(J2D_TRACE_INFO, "GDIWindowSurfaceData_initIDs"); nullbrush = (HBRUSH) ::GetStockObject(NULL_BRUSH); nullpen = (HPEN) ::GetStockObject(NULL_PEN); initThreadInfoIndex(); xorCompClass = (jclass)env->NewGlobalRef(XORComp); if (env->ExceptionCheck()) { return; } tc = env->FindClass("java/lang/Thread"); DASSERT(tc != NULL); CHECK_NULL(tc); threadClass = (jclass)env->NewGlobalRef(tc); DASSERT(threadClass != NULL); CHECK_NULL(threadClass); currentThreadMethodID = env->GetStaticMethodID(threadClass, "currentThread", "()Ljava/lang/Thread;"); DASSERT(currentThreadMethodID != NULL); } #undef ExceptionOccurred /* * Class: sun_java2d_windows_GDIWindowSurfaceData * Method: initOps * Signature: (Ljava/lang/Object;IIIIII)V */ JNIEXPORT void JNICALL Java_sun_java2d_windows_GDIWindowSurfaceData_initOps(JNIEnv *env, jobject wsd, jobject peer, jint depth, jint redMask, jint greenMask, jint blueMask, jint screen) { J2dTraceLn(J2D_TRACE_INFO, "GDIWindowSurfaceData_initOps"); GDIWinSDOps *wsdo = (GDIWinSDOps *)SurfaceData_InitOps(env, wsd, sizeof(GDIWinSDOps)); if (wsdo == NULL) { JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed."); return; } wsdo->timeStamp = InterlockedIncrement(&timeStamp); //creation time stamp wsdo->sdOps.Lock = GDIWinSD_Lock; wsdo->sdOps.GetRasInfo = GDIWinSD_GetRasInfo; wsdo->sdOps.Unlock = GDIWinSD_Unlock; wsdo->sdOps.Dispose = GDIWinSD_Dispose; wsdo->sdOps.Setup = GDIWinSD_Setup; wsdo->GetDC = GDIWinSD_GetDC; wsdo->ReleaseDC = GDIWinSD_ReleaseDC; wsdo->InvalidateSD = GDIWinSD_InvalidateSD; wsdo->invalid = JNI_FALSE; wsdo->lockType = WIN32SD_LOCK_UNLOCKED; wsdo->peer = env->NewWeakGlobalRef(peer); if (env->ExceptionOccurred()) { return; } wsdo->depth = depth; wsdo->pixelMasks[0] = redMask; wsdo->pixelMasks[1] = greenMask; wsdo->pixelMasks[2] = blueMask; // Init the DIB pixelStride and pixel masks according to // the pixel depth. In the 8-bit case, there are no // masks as a palette DIB is used instead. Likewise // in the 24-bit case, Windows doesn't expect the masks switch (depth) { case 8: wsdo->pixelStride = 1; break; case 15: //555 wsdo->pixelStride = 2; break; case 16: //565 wsdo->pixelStride = 2; break; case 24: wsdo->pixelStride = 3; break; case 32: //888 wsdo->pixelStride = 4; break; } // GDIWindowSurfaceData_GetWindow will throw NullPointerException // if wsdo->window is NULL wsdo->window = GDIWindowSurfaceData_GetWindow(env, wsdo); J2dTraceLn2(J2D_TRACE_VERBOSE, " wsdo=0x%x wsdo->window=0x%x", wsdo, wsdo->window); { Devices::InstanceAccess devices; wsdo->device = devices->GetDeviceReference(screen, FALSE); } if (wsdo->device == NULL || !SurfaceDepthsCompatible(depth, wsdo->device->GetBitDepth())) { if (wsdo->device != NULL) { J2dTraceLn2(J2D_TRACE_WARNING, "GDIWindowSurfaceData_initOps: Surface depth mismatch: "\ "wsdo->depth=%d device depth=%d. Surface invalidated.", wsdo->depth, wsdo->device->GetBitDepth()); } else { J2dTraceLn1(J2D_TRACE_WARNING, "GDIWindowSurfaceData_initOps: Incorrect "\ "screen number (screen=%d). Surface invalidated.", screen); } wsdo->invalid = JNI_TRUE; } wsdo->surfaceLock = new CriticalSection(); wsdo->bitmap = NULL; wsdo->bmdc = NULL; wsdo->bmCopyToScreen = FALSE; } JNIEXPORT GDIWinSDOps * JNICALL GDIWindowSurfaceData_GetOps(JNIEnv *env, jobject sData) { SurfaceDataOps *ops = SurfaceData_GetOps(env, sData); // REMIND: There was originally a condition check here to make sure // that we were really dealing with a GDIWindowSurfaceData object, but // it did not allow for the existence of other win32-accelerated // surface data objects (e.g., Win32OffScreenSurfaceData). I've // removed the check for now, but we should replace it with another, // more general check against Win32-related surfaces. return (GDIWinSDOps *) ops; } JNIEXPORT GDIWinSDOps * JNICALL GDIWindowSurfaceData_GetOpsNoSetup(JNIEnv *env, jobject sData) { // use the 'no setup' version of GetOps SurfaceDataOps *ops = SurfaceData_GetOpsNoSetup(env, sData); return (GDIWinSDOps *) ops; } JNIEXPORT AwtComponent * JNICALL GDIWindowSurfaceData_GetComp(JNIEnv *env, GDIWinSDOps *wsdo) { PDATA pData; jobject localObj = env->NewLocalRef(wsdo->peer); if (localObj == NULL || (pData = JNI_GET_PDATA(localObj)) == NULL) { J2dTraceLn1(J2D_TRACE_WARNING, "GDIWindowSurfaceData_GetComp: Null pData? pData=0x%x", pData); if (beingShutdown == JNI_TRUE) { wsdo->invalid = JNI_TRUE; return (AwtComponent *) NULL; } try { AwtToolkit::GetInstance().VerifyActive(); } catch (awt_toolkit_shutdown&) { beingShutdown = JNI_TRUE; wsdo->invalid = JNI_TRUE; return (AwtComponent *) NULL; } if (wsdo->invalid == JNI_TRUE) { SurfaceData_ThrowInvalidPipeException(env, "GDIWindowSurfaceData: bounds changed"); } else { JNU_ThrowNullPointerException(env, "component argument pData"); } return (AwtComponent *) NULL; } return static_cast(pData); } JNIEXPORT HWND JNICALL GDIWindowSurfaceData_GetWindow(JNIEnv *env, GDIWinSDOps *wsdo) { HWND window = wsdo->window; if (window == (HWND) NULL) { AwtComponent *comp = GDIWindowSurfaceData_GetComp(env, wsdo); if (comp == NULL) { J2dTraceLn(J2D_TRACE_WARNING, "GDIWindowSurfaceData_GetWindow: null component"); return (HWND) NULL; } comp->GetInsets(&wsdo->insets); window = comp->GetHWnd(); if (::IsWindow(window) == FALSE) { J2dRlsTraceLn(J2D_TRACE_ERROR, "GDIWindowSurfaceData_GetWindow: disposed component"); JNU_ThrowNullPointerException(env, "disposed component"); return (HWND) NULL; } wsdo->window = window; } return window; } } /* extern "C" */ static jboolean GDIWinSD_SimpleClip(JNIEnv *env, GDIWinSDOps *wsdo, SurfaceDataBounds *bounds, HDC hDC) { RECT rClip; J2dTraceLn(J2D_TRACE_INFO, "GDIWinSD_SimpleClip"); if (hDC == NULL) { return JNI_FALSE; } int nComplexity = ::GetClipBox(hDC, &rClip); switch (nComplexity) { case COMPLEXREGION: { J2dTraceLn(J2D_TRACE_VERBOSE, " complex clipping region"); // if complex user/system clip, more detailed testing required // check to see if the view itself has a complex clip. // ::GetClipBox is only API which returns overlapped window status // so we set the rView as our clip, and then see if resulting // clip is complex. // Only other way to figure this out would be to walk the // overlapping windows (no API to get the actual visible clip // list). Then we'd still have to merge that info with the // clip region for the dc (if it exists). // REMIND: we can cache the CreateRectRgnIndirect result, // and only override with ::SetRectRgn // First, create a region handle (need existing HRGN for // the following call). HRGN rgnSave = ::CreateRectRgn(0, 0, 0, 0); int clipStatus = ::GetClipRgn(hDC, rgnSave); if (-1 == clipStatus) { J2dTraceLn(J2D_TRACE_WARNING, "GDIWinSD_SimpleClip: failed due to clip status"); ::DeleteObject(rgnSave); return JNI_FALSE; } HRGN rgnBounds = ::CreateRectRgn( bounds->x1 - wsdo->insets.left, bounds->y1 - wsdo->insets.top, bounds->x2 - wsdo->insets.left, bounds->y2 - wsdo->insets.top); ::SelectClipRgn(hDC, rgnBounds); nComplexity = ::GetClipBox(hDC, &rClip); ::SelectClipRgn(hDC, clipStatus? rgnSave: NULL); ::DeleteObject(rgnSave); ::DeleteObject(rgnBounds); // Now, test the new clip box. If it's still not a // SIMPLE region, then our bounds must intersect part of // the clipping article if (SIMPLEREGION != nComplexity) { J2dTraceLn(J2D_TRACE_WARNING, "GDIWinSD_SimpleClip: failed due to complexity"); return JNI_FALSE; } } // NOTE: No break here - we want to fall through into the // SIMPLE case, adjust our bounds by the new rClip rect // and make sure that our locking bounds are not empty. case SIMPLEREGION: J2dTraceLn(J2D_TRACE_VERBOSE, " simple clipping region"); // Constrain the bounds to the given clip box if (bounds->x1 < rClip.left) { bounds->x1 = rClip.left; } if (bounds->y1 < rClip.top) { bounds->y1 = rClip.top; } if (bounds->x2 > rClip.right) { bounds->x2 = rClip.right; } if (bounds->y2 > rClip.bottom) { bounds->y2 = rClip.bottom; } // If the bounds are 0 or negative, then the bounds have // been obscured by the clip box, so return FALSE if ((bounds->x2 <= bounds->x1) || (bounds->y2 <= bounds->y1)) { // REMIND: We should probably do something different here // instead of simply returning FALSE. Since the bounds are // empty we won't end up drawing anything, so why spend the // effort of returning false and having GDI do a LOCK_BY_DIB? // Perhaps we need a new lock code that will indicate that we // shouldn't bother drawing? J2dTraceLn(J2D_TRACE_WARNING, "GDIWinSD_SimpleClip: failed due to empty bounds"); return JNI_FALSE; } break; case NULLREGION: case ERROR: default: J2dTraceLn1(J2D_TRACE_ERROR, "GDIWinSD_SimpleClip: failed due to incorrect complexity=%d", nComplexity); return JNI_FALSE; } return JNI_TRUE; } static jint GDIWinSD_Lock(JNIEnv *env, SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo, jint lockflags) { GDIWinSDOps *wsdo = (GDIWinSDOps *) ops; int ret = SD_SUCCESS; HDC hDC; J2dTraceLn(J2D_TRACE_INFO, "GDIWinSD_Lock"); /* This surfaceLock replaces an earlier implementation which used a monitor associated with the peer. That implementation was prone to deadlock problems, so it was replaced by a lock that does not have dependencies outside of this thread or object. However, this lock doesn't necessarily do all that we want. For example, a user may issue a call which results in a DIB lock and another call which results in a DDraw Blt. We can't guarantee what order these operations happen in (they are driver and video-card dependent), so locking around the issue of either of those calls won't necessarily guarantee a particular result. The real solution might be to move away from mixing our rendering API's. That is, if we only used DDraw, then we could guarantee that all rendering operations would happen in a given order. Similarly for GDI. But by mixing them, we leave our code at the mercy of driver bugs.*/ wsdo->surfaceLock->Enter(); if (wsdo->invalid == JNI_TRUE) { J2dTraceLn(J2D_TRACE_WARNING, "GDIWinSD_Lock: surface is invalid"); wsdo->surfaceLock->Leave(); if (beingShutdown != JNI_TRUE) { SurfaceData_ThrowInvalidPipeException(env, "GDIWindowSurfaceData: bounds changed"); } return SD_FAILURE; } if (wsdo->lockType != WIN32SD_LOCK_UNLOCKED) { wsdo->surfaceLock->Leave(); if (!safe_ExceptionOccurred(env)) { JNU_ThrowInternalError(env, "Win32 LockRasData cannot nest locks"); } return SD_FAILURE; } hDC = wsdo->GetDC(env, wsdo, 0, NULL, NULL, NULL, 0); if (hDC == NULL) { wsdo->surfaceLock->Leave(); if (beingShutdown != JNI_TRUE) { JNU_ThrowNullPointerException(env, "HDC for component"); } return SD_FAILURE; } if (lockflags & SD_LOCK_RD_WR) { // Do an initial clip to the client region of the window RECT crect; ::GetClientRect(wsdo->window, &crect); // Translate to window coords crect.left += wsdo->insets.left; crect.top += wsdo->insets.top; crect.right += wsdo->insets.left; crect.bottom += wsdo->insets.top; SurfaceDataBounds *bounds = &pRasInfo->bounds; if (bounds->x1 < crect.left) { bounds->x1 = crect.left; } if (bounds->y1 < crect.top) { bounds->y1 = crect.top; } if (bounds->x2 > crect.right) { bounds->x2 = crect.right; } if (bounds->y2 > crect.bottom) { bounds->y2 = crect.bottom; } if (bounds->x2 > bounds->x1 && bounds->y2 > bounds->y1) { wsdo->lockType = WIN32SD_LOCK_BY_DIB; if (lockflags & SD_LOCK_FASTEST) { ret = SD_SLOWLOCK; } J2dTraceLn(J2D_TRACE_VERBOSE, " locked by DIB"); } else { wsdo->ReleaseDC(env, wsdo, hDC); wsdo->lockType = WIN32SD_LOCK_UNLOCKED; wsdo->surfaceLock->Leave(); ret = SD_FAILURE; J2dTraceLn(J2D_TRACE_ERROR, "GDIWinSD_Lock: error locking by DIB"); } } else { J2dTraceLn(J2D_TRACE_VERBOSE, "GDIWinSD_Lock: surface wasn't locked"); /* They didn't lock for anything - we won't give them anything */ wsdo->ReleaseDC(env, wsdo, hDC); wsdo->lockType = WIN32SD_LOCK_UNLOCKED; wsdo->surfaceLock->Leave(); ret = SD_FAILURE; } wsdo->lockFlags = lockflags; return ret; } static void GDIWinSD_GetRasInfo(JNIEnv *env, SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo) { GDIWinSDOps *wsdo = (GDIWinSDOps *) ops; jint lockflags = wsdo->lockFlags; J2dTraceLn(J2D_TRACE_INFO, "GDIWinSD_GetRasInfo"); HDC hDC = GetThreadDC(env, wsdo); if (wsdo->lockType == WIN32SD_LOCK_UNLOCKED) { memset(pRasInfo, 0, sizeof(*pRasInfo)); return; } if (wsdo->lockType == WIN32SD_LOCK_BY_DIB) { int x, y, w, h; int pixelStride = wsdo->pixelStride; // do not subtract insets from x,y as we take care of it in SD_GetDC x = pRasInfo->bounds.x1; y = pRasInfo->bounds.y1; w = pRasInfo->bounds.x2 - x; h = pRasInfo->bounds.y2 - y; struct tagBitmapheader { BITMAPINFOHEADER bmiHeader; union { DWORD dwMasks[3]; RGBQUAD palette[256]; } colors; } bmi; // Need to create bitmap if we don't have one already or // if the existing one is not large enough for this operation // or if we are in 8 bpp display mode (because we need to // make sure that the latest palette info gets loaded into // the bitmap) // REMIND: we should find some way to dynamically force bitmap // recreation only when the palette changes if (pixelStride == 1 || !wsdo->bitmap || (w > wsdo->bmWidth) || (h > wsdo->bmHeight)) { if (wsdo->bitmap) { // delete old objects J2dTraceLn(J2D_TRACE_VERBOSE, "GDIWinSD_GetRasInfo: recreating GDI bitmap"); if (wsdo->bmdc) { // should not be null ::SelectObject(wsdo->bmdc, wsdo->oldmap); ::DeleteDC(wsdo->bmdc); wsdo->bmdc = 0; } ::DeleteObject(wsdo->bitmap); wsdo->bitmap = 0; } bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); bmi.bmiHeader.biWidth = w; bmi.bmiHeader.biHeight = -h; wsdo->bmWidth = w; wsdo->bmHeight = h; bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biBitCount = pixelStride * 8; // 1,3 byte use BI_RGB, 2,4 byte use BI_BITFIELD... bmi.bmiHeader.biCompression = (pixelStride & 1) ? BI_RGB : BI_BITFIELDS; bmi.bmiHeader.biSizeImage = 0; bmi.bmiHeader.biXPelsPerMeter = 0; bmi.bmiHeader.biYPelsPerMeter = 0; bmi.bmiHeader.biClrUsed = 0; bmi.bmiHeader.biClrImportant = 0; if (pixelStride == 1) { // we can use systemEntries here because // RGBQUAD is xRGB and systemEntries are stored as xRGB memcpy(bmi.colors.palette, wsdo->device->GetSystemPaletteEntries(), sizeof(bmi.colors.palette)); } else { // For non-index cases, init the masks for the pixel depth for (int i = 0; i < 3; i++) { bmi.colors.dwMasks[i] = wsdo->pixelMasks[i]; } } // REMIND: This would be better if moved to the Lock function // so that errors could be dealt with. wsdo->bitmap = ::CreateDIBSection(hDC, (BITMAPINFO *) &bmi, DIB_RGB_COLORS, &wsdo->bmBuffer, NULL, 0); if (wsdo->bitmap != 0) { // scanStride is cached along with reuseable bitmap // Round up to the next DWORD boundary wsdo->bmScanStride = (wsdo->bmWidth * pixelStride + 3) & ~3; wsdo->bmdc = ::CreateCompatibleDC(hDC); if (wsdo->bmdc == 0) { ::DeleteObject(wsdo->bitmap); wsdo->bitmap = 0; } else { wsdo->oldmap = (HBITMAP) ::SelectObject(wsdo->bmdc, wsdo->bitmap); } } } if (wsdo->bitmap != 0) { if (lockflags & SD_LOCK_NEED_PIXELS) { int ret = ::BitBlt(wsdo->bmdc, 0, 0, w, h, hDC, x, y, SRCCOPY); ::GdiFlush(); } wsdo->x = x; wsdo->y = y; wsdo->w = w; wsdo->h = h; pRasInfo->rasBase = (char *)wsdo->bmBuffer - (x*pixelStride + y*wsdo->bmScanStride); pRasInfo->pixelStride = pixelStride; pRasInfo->pixelBitOffset = 0; pRasInfo->scanStride = wsdo->bmScanStride; if (lockflags & SD_LOCK_WRITE) { // If the user writes to the bitmap then we should // copy the bitmap to the screen during Unlock wsdo->bmCopyToScreen = TRUE; } } else { pRasInfo->rasBase = NULL; pRasInfo->pixelStride = 0; pRasInfo->pixelBitOffset = 0; pRasInfo->scanStride = 0; } } else { /* They didn't lock for anything - we won't give them anything */ pRasInfo->rasBase = NULL; pRasInfo->pixelStride = 0; pRasInfo->pixelBitOffset = 0; pRasInfo->scanStride = 0; } if (wsdo->lockFlags & SD_LOCK_LUT) { pRasInfo->lutBase = (long *) wsdo->device->GetSystemPaletteEntries(); pRasInfo->lutSize = 256; } else { pRasInfo->lutBase = NULL; pRasInfo->lutSize = 0; } if (wsdo->lockFlags & SD_LOCK_INVCOLOR) { pRasInfo->invColorTable = wsdo->device->GetSystemInverseLUT(); ColorData *cData = wsdo->device->GetColorData(); pRasInfo->redErrTable = cData->img_oda_red; pRasInfo->grnErrTable = cData->img_oda_green; pRasInfo->bluErrTable = cData->img_oda_blue; } else { pRasInfo->invColorTable = NULL; pRasInfo->redErrTable = NULL; pRasInfo->grnErrTable = NULL; pRasInfo->bluErrTable = NULL; } if (wsdo->lockFlags & SD_LOCK_INVGRAY) { pRasInfo->invGrayTable = wsdo->device->GetColorData()->pGrayInverseLutData; } else { pRasInfo->invGrayTable = NULL; } } static void GDIWinSD_Setup(JNIEnv *env, SurfaceDataOps *ops) { // Call SetupTGI to ensure that this thread already has a DC that is // compatible with this window. This means that we won't be calling // ::SendMessage(GETDC) in the middle of a lock procedure, which creates // a potential deadlock situation. // Note that calling SetupTGI here means that anybody needing a DC // later in this rendering process need only call GetTGI, which // assumes that the TGI structure is valid for this thread/window. SetupThreadGraphicsInfo(env, (GDIWinSDOps*)ops); } static void GDIWinSD_Unlock(JNIEnv *env, SurfaceDataOps *ops, SurfaceDataRasInfo *pRasInfo) { GDIWinSDOps *wsdo = (GDIWinSDOps *) ops; J2dTraceLn(J2D_TRACE_INFO, "GDIWinSD_Unlock"); HDC hDC = GetThreadDC(env, wsdo); if (wsdo->lockType == WIN32SD_LOCK_UNLOCKED) { if (!safe_ExceptionOccurred(env)) { JNU_ThrowInternalError(env, "Unmatched unlock on Win32 SurfaceData"); } return; } if (wsdo->lockType == WIN32SD_LOCK_BY_DIB) { if (wsdo->lockFlags & SD_LOCK_WRITE) { J2dTraceLn(J2D_TRACE_VERBOSE, "GDIWinSD_Unlock: do Blt of the bitmap"); if (wsdo->bmCopyToScreen && ::IsWindowVisible(wsdo->window)) { // Don't bother copying to screen if our window has gone away // or if the bitmap was not actually written to during this // Lock/Unlock procedure. ::BitBlt(hDC, wsdo->x, wsdo->y, wsdo->w, wsdo->h, wsdo->bmdc, 0, 0, SRCCOPY); ::GdiFlush(); } wsdo->bmCopyToScreen = FALSE; } wsdo->lockType = WIN32SD_LOCK_UNLOCKED; wsdo->ReleaseDC(env, wsdo, hDC); } wsdo->surfaceLock->Leave(); } /* * REMIND: This mechanism is just a prototype of a way to manage a * small cache of DC objects. It is incomplete in the following ways: * * - It is not thread-safe! It needs appropriate locking and release calls * (perhaps the AutoDC mechanisms from Kestrel) * - It does hardly any error checking (What if GetDCEx returns NULL?) * - It cannot handle printer DCs and their resolution * - It should probably "live" in the native SurfaceData object to allow * alternate implementations for printing and embedding * - It doesn't handle XOR * - It caches the client bounds to determine if clipping is really needed * (no way to invalidate the cached bounds and there is probably a better * way to manage clip validation in any case) */ #define COLORFOR(c) (PALETTERGB(((c)>>16)&0xff,((c)>>8)&0xff,((c)&0xff))) COLORREF CheckGrayColor(GDIWinSDOps *wsdo, int c) { if (wsdo->device->GetGrayness() != GS_NOTGRAY) { int g = (77 *(c & 0xFF) + 150*((c >> 8) & 0xFF) + 29 *((c >> 16) & 0xFF) + 128) / 256; c = g | (g << 8) | (g << 16); } return COLORFOR(c); } static HDC GDIWinSD_GetDC(JNIEnv *env, GDIWinSDOps *wsdo, jint type, jint *patrop, jobject clip, jobject comp, jint color) { J2dTraceLn(J2D_TRACE_INFO, "GDIWinSD_GetDC"); if (wsdo->invalid == JNI_TRUE) { if (beingShutdown != JNI_TRUE) { SurfaceData_ThrowInvalidPipeException(env, "bounds changed"); } return (HDC) NULL; } ThreadGraphicsInfo *info = GetThreadGraphicsInfo(env, wsdo); GDIWinSD_InitDC(env, wsdo, info, type, patrop, clip, comp, color); return env->ExceptionCheck() ? (HDC)NULL : info->hDC; } JNIEXPORT void JNICALL GDIWinSD_InitDC(JNIEnv *env, GDIWinSDOps *wsdo, ThreadGraphicsInfo *info, jint type, jint *patrop, jobject clip, jobject comp, jint color) { J2dTraceLn(J2D_TRACE_INFO, "GDIWinSD_InitDC"); // init clip if (clip == NULL) { if (info->type & CLIP) { ::SelectClipRgn(info->hDC, (HRGN) NULL); info->type ^= CLIP; } if (info->clip != NULL) { env->DeleteWeakGlobalRef(info->clip); info->clip = NULL; } } else if (!env->IsSameObject(clip, info->clip)) { SurfaceDataBounds span; RegionData clipInfo; if (Region_GetInfo(env, clip, &clipInfo)) { // return; // REMIND: What to do here? } if (Region_IsEmpty(&clipInfo)) { HRGN hrgn = ::CreateRectRgn(0, 0, 0, 0); ::SelectClipRgn(info->hDC, hrgn); ::DeleteObject(hrgn); info->type |= CLIP; } else if (Region_IsRectangular(&clipInfo)) { if (clipInfo.bounds.x1 <= info->bounds.left && clipInfo.bounds.y1 <= info->bounds.top && clipInfo.bounds.x2 >= info->bounds.right && clipInfo.bounds.y2 >= info->bounds.bottom) { if (info->type & CLIP) { ::SelectClipRgn(info->hDC, (HRGN) NULL); info->type ^= CLIP; } } else { // Make the window-relative rect a client-relative // one for Windows HRGN hrgn = ::CreateRectRgn(clipInfo.bounds.x1 - wsdo->insets.left, clipInfo.bounds.y1 - wsdo->insets.top, clipInfo.bounds.x2 - wsdo->insets.left, clipInfo.bounds.y2 - wsdo->insets.top); ::SelectClipRgn(info->hDC, hrgn); ::DeleteObject(hrgn); info->type |= CLIP; } } else { int leftInset = wsdo->insets.left; int topInset = wsdo->insets.top; Region_StartIteration(env, &clipInfo); jint numrects = Region_CountIterationRects(&clipInfo); RGNDATA *lpRgnData; try { lpRgnData = (RGNDATA *) SAFE_SIZE_STRUCT_ALLOC(safe_Malloc, sizeof(RGNDATAHEADER), numrects, sizeof(RECT)); } catch (std::bad_alloc&) { JNU_ThrowOutOfMemoryError(env, "Initialization of surface region data failed."); return; } const DWORD nCount = sizeof(RGNDATAHEADER) + numrects * sizeof(RECT); lpRgnData->rdh.dwSize = sizeof(RGNDATAHEADER); lpRgnData->rdh.iType = RDH_RECTANGLES; lpRgnData->rdh.nCount = numrects; lpRgnData->rdh.nRgnSize = 0; lpRgnData->rdh.rcBound.left = clipInfo.bounds.x1 - leftInset; lpRgnData->rdh.rcBound.top = clipInfo.bounds.y1 - topInset; lpRgnData->rdh.rcBound.right = clipInfo.bounds.x2 - leftInset; lpRgnData->rdh.rcBound.bottom = clipInfo.bounds.y2 - topInset; RECT *pRect = (RECT *) &(((RGNDATA *)lpRgnData)->Buffer); while (Region_NextIteration(&clipInfo, &span)) { pRect->left = span.x1 - leftInset; pRect->top = span.y1 - topInset; pRect->right = span.x2 - leftInset; pRect->bottom = span.y2 - topInset; pRect++; } Region_EndIteration(env, &clipInfo); HRGN hrgn = ::ExtCreateRegion(NULL, nCount, lpRgnData); free(lpRgnData); ::SelectClipRgn(info->hDC, hrgn); ::DeleteObject(hrgn); info->type |= CLIP; } if (info->clip != NULL) { env->DeleteWeakGlobalRef(info->clip); } info->clip = env->NewWeakGlobalRef(clip); if (env->ExceptionCheck()) { return; } } // init composite if ((comp == NULL) || !env->IsInstanceOf(comp, xorCompClass)) { if (info->comp != NULL) { env->DeleteWeakGlobalRef(info->comp); info->comp = NULL; info->patrop = PATCOPY; ::SetROP2(info->hDC, R2_COPYPEN); } } else { if (!env->IsSameObject(comp, info->comp)) { info->xorcolor = GrPrim_CompGetXorColor(env, comp); if (info->comp != NULL) { env->DeleteWeakGlobalRef(info->comp); } info->comp = env->NewWeakGlobalRef(comp); info->patrop = PATINVERT; ::SetROP2(info->hDC, R2_XORPEN); } color ^= info->xorcolor; } if (patrop != NULL) { *patrop = info->patrop; } // init brush and pen if (type & BRUSH) { if (info->brushclr != color || (info->brush == NULL)) { if (info->type & BRUSH) { ::SelectObject(info->hDC, nullbrush); info->type ^= BRUSH; } if (info->brush != NULL) { info->brush->Release(); } info->brush = AwtBrush::Get(CheckGrayColor(wsdo, color)); info->brushclr = color; } if ((info->type & BRUSH) == 0) { ::SelectObject(info->hDC, info->brush->GetHandle()); info->type ^= BRUSH; } } else if (type & NOBRUSH) { if (info->type & BRUSH) { ::SelectObject(info->hDC, nullbrush); info->type ^= BRUSH; } } if (type & PEN) { if (info->penclr != color || (info->pen == NULL)) { if (info->type & PEN) { ::SelectObject(info->hDC, nullpen); info->type ^= PEN; } if (info->pen != NULL) { info->pen->Release(); } info->pen = AwtPen::Get(CheckGrayColor(wsdo, color)); info->penclr = color; } if ((info->type & PEN) == 0) { ::SelectObject(info->hDC, info->pen->GetHandle()); info->type ^= PEN; } } else if (type & NOPEN) { if (info->type & PEN) { ::SelectObject(info->hDC, nullpen); info->type ^= PEN; } } } static void GDIWinSD_ReleaseDC(JNIEnv *env, GDIWinSDOps *wsdo, HDC hDC) { J2dTraceLn(J2D_TRACE_INFO, "GDIWinSD_ReleaseDC"); // Don't actually do anything here: every thread holds its own // wsdo-specific DC until the thread goes away or the wsdo // is disposed. } static void GDIWinSD_InvalidateSD(JNIEnv *env, GDIWinSDOps *wsdo) { J2dTraceLn(J2D_TRACE_INFO, "GDIWinSD_InvalidateSD"); J2dTraceLn2(J2D_TRACE_VERBOSE, " wsdo=0x%x wsdo->window=0x%x", wsdo, wsdo->window); wsdo->invalid = JNI_TRUE; } /* * Method: GDIWinSD_Dispose */ static void GDIWinSD_Dispose(JNIEnv *env, SurfaceDataOps *ops) { J2dTraceLn(J2D_TRACE_INFO, "GDIWinSD_Dispose"); // ops is assumed non-null as it is checked in SurfaceData_DisposeOps GDIWinSDOps *wsdo = (GDIWinSDOps*)ops; if (wsdo->bitmap) { // delete old objects J2dTraceLn(J2D_TRACE_VERBOSE, " disposing the GDI bitmap"); if (wsdo->bmdc) { // should not be null ::SelectObject(wsdo->bmdc, wsdo->oldmap); ::DeleteDC(wsdo->bmdc); wsdo->bmdc = 0; } ::DeleteObject(wsdo->bitmap); wsdo->bitmap = 0; } env->DeleteWeakGlobalRef(wsdo->peer); if (wsdo->device != NULL) { wsdo->device->Release(); wsdo->device = NULL; } delete wsdo->surfaceLock; } /* * Class: sun_java2d_windows_GDIWindowSurfaceData * Method: invalidateSD * Signature: ()V */ JNIEXPORT void JNICALL Java_sun_java2d_windows_GDIWindowSurfaceData_invalidateSD(JNIEnv *env, jobject wsd) { J2dTraceLn(J2D_TRACE_INFO, "GDIWindowSurfaceData_invalidateSD"); GDIWinSDOps *wsdo = GDIWindowSurfaceData_GetOpsNoSetup(env, wsd); if (wsdo != NULL) { wsdo->InvalidateSD(env, wsdo); } }