/*
 * Copyright (c) 2007, 2024, 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 "D3DPipeline.h"
#include <jlong.h>
#include "D3DSurfaceData.h"
#include "D3DPipelineManager.h"
#include "Trace.h"
#include "awt_Toolkit.h"
#include "awt_Window.h"
#include "awt_BitmapUtil.h"
#include "D3DRenderQueue.h"


// REMIND: move to awt_Component.h
extern "C" HWND AwtComponent_GetHWnd(JNIEnv *env, jlong pData);

/* This looks weird. but since some AWT headers need to be included,
 * we end up with AWT's alloc.h macro definition of ExceptionOccurred.
 * The reasons for that re-defintion do not apply to this code, so undef it.
 */
#undef ExceptionOccurred

/**
 * Initializes nativeWidth/Height fields of the SurfaceData object with
 * dimensions on the native surface.
 */
void D3DSD_SetNativeDimensions(JNIEnv *env, D3DSDOps *d3dsdo) {
    jobject sdObject;
    jint width, height;

    RETURN_IF_NULL(sdObject = env->NewLocalRef(d3dsdo->sdOps.sdObject));

    if (d3dsdo->pResource != NULL) {
        width = d3dsdo->pResource->GetDesc()->Width;
        height = d3dsdo->pResource->GetDesc()->Height;
    } else {
        width = d3dsdo->width;
        height = d3dsdo->height;
    }

    JNU_SetFieldByName(env, NULL, sdObject, "nativeWidth", "I", width);
    if (!(env->ExceptionCheck())) {
        JNU_SetFieldByName(env, NULL, sdObject, "nativeHeight", "I", height);
    }

    env->DeleteLocalRef(sdObject);
}

void D3DSD_Flush(void *pData)
{
    J2dTraceLn(J2D_TRACE_INFO, "D3DSD_Flush");
    RETURN_IF_NULL(pData);

    D3DSDOps *d3dsdo = (D3DSDOps*)pData;
    if (d3dsdo->pResource != NULL) {
        D3DContext *pCtx;
        D3DPipelineManager *pMgr;

        d3dsdo->pResource->SetSDOps(NULL);

        if ((pMgr = D3DPipelineManager::GetInstance()) != NULL &&
            SUCCEEDED(pMgr->GetD3DContext(d3dsdo->adapter, &pCtx)))
        {
            if (pCtx->GetResourceManager()) {
                pCtx->GetResourceManager()->ReleaseResource(d3dsdo->pResource);
            }
        }
        d3dsdo->pResource = NULL;
    }
}

void
D3DSD_MarkLost(void *pData)
{
    D3DSDOps *d3dsdo;
    jobject sdObject;
    JNIEnv *env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);

    J2dTraceLn(J2D_TRACE_INFO, "D3DSD_MarkLost");

    RETURN_IF_NULL(pData);

    d3dsdo = (D3DSDOps*)pData;
    RETURN_IF_NULL(sdObject = env->NewLocalRef(d3dsdo->sdOps.sdObject));

    JNU_CallMethodByName(env, NULL, sdObject,
                         "setSurfaceLost", "(Z)V", JNI_TRUE);

    env->DeleteLocalRef(sdObject);
}

// ------------ generic SurfaceData.h functions ----------------

void
D3DSD_Dispose(JNIEnv *env, SurfaceDataOps *ops)
{
    D3DSDOps *d3dsdo = (D3DSDOps *)ops;
    RETURN_IF_NULL(d3dsdo);

    JNU_CallStaticMethodByName(env, NULL, "sun/java2d/d3d/D3DSurfaceData",
                               "dispose", "(J)V",
                               ptr_to_jlong(ops));
}

/**
 * This is the implementation of the general surface LockFunc defined in
 * SurfaceData.h.
 */
jint
D3DSD_Lock(JNIEnv *env,
           SurfaceDataOps *ops,
           SurfaceDataRasInfo *pRasInfo,
           jint lockflags)
{
    JNU_ThrowInternalError(env, "D3DSD_Lock not implemented!");
    return SD_FAILURE;
}

/**
 * This is the implementation of the general GetRasInfoFunc defined in
 * SurfaceData.h.
 */
void
D3DSD_GetRasInfo(JNIEnv *env,
                 SurfaceDataOps *ops,
                 SurfaceDataRasInfo *pRasInfo)
{
    JNU_ThrowInternalError(env, "D3DSD_GetRasInfo not implemented!");
}

/**
 * This is the implementation of the general surface UnlockFunc defined in
 * SurfaceData.h.
 */
void
D3DSD_Unlock(JNIEnv *env,
             SurfaceDataOps *ops,
             SurfaceDataRasInfo *pRasInfo)
{
    JNU_ThrowInternalError(env, "D3DSD_Unlock not implemented!");
}

// ------------ D3DSurfaceData's JNI methods ----------------


extern "C" {

/*
 * Class:     sun_java2d_d3d_D3DSurfaceData
 * Method:    initOps
 * Signature: (III)V
 */
JNIEXPORT void
JNICALL Java_sun_java2d_d3d_D3DSurfaceData_initOps
  (JNIEnv *env, jobject d3dsd, jint gdiScreen, jint width, jint height)
{
    D3DPipelineManager *pMgr;
    D3DSDOps *d3dsdo = (D3DSDOps *)SurfaceData_InitOps(env, d3dsd,
                                                       sizeof(D3DSDOps));

    J2dTraceLn(J2D_TRACE_INFO, "D3DSurfaceData_initOps");

    if (d3dsdo == NULL) {
        JNU_ThrowOutOfMemoryError(env, "creating native d3d ops");
        return;
    }

    d3dsdo->sdOps.Lock       = D3DSD_Lock;
    d3dsdo->sdOps.GetRasInfo = D3DSD_GetRasInfo;
    d3dsdo->sdOps.Unlock     = D3DSD_Unlock;
    d3dsdo->sdOps.Dispose    = D3DSD_Dispose;

    d3dsdo->xoff = 0;
    d3dsdo->yoff = 0;
    d3dsdo->width = width;
    d3dsdo->height = height;

    d3dsdo->pResource = NULL;

    d3dsdo->adapter =
        (pMgr = D3DPipelineManager::GetInstance()) == NULL ?
            D3DADAPTER_DEFAULT :
            pMgr->GetAdapterOrdinalForScreen(gdiScreen);
}


/*
 * Class:     sun_java2d_d3d_D3DSurfaceData
 * Method:    initTexture
 * Signature: (JZZ)Z
 */
JNIEXPORT jboolean
JNICALL Java_sun_java2d_d3d_D3DSurfaceData_initTexture
  (JNIEnv *env, jobject d3dsd,
  jlong pData, jboolean isRTT, jboolean isOpaque)
{
    HRESULT res;
    D3DSDOps *d3dsdo;
    D3DContext *pCtx;
    D3DPipelineManager *pMgr;
    D3DFORMAT format;

    J2dTraceLn(J2D_TRACE_INFO, "D3DSurfaceData_initTexture");

    RETURN_STATUS_IF_NULL(d3dsdo = (D3DSDOps *)jlong_to_ptr(pData), JNI_FALSE);
    RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), JNI_FALSE);

    if (FAILED(res = pMgr->GetD3DContext(d3dsdo->adapter, &pCtx))) {
        D3DRQ_MarkLostIfNeeded(res, d3dsdo);
        return JNI_FALSE;
    }
    RETURN_STATUS_IF_NULL(pCtx->GetResourceManager(), JNI_FALSE);

    pCtx->GetResourceManager()->ReleaseResource(d3dsdo->pResource);
    d3dsdo->pResource = NULL;

    if (isRTT && isOpaque) {
        format = pCtx->GetPresentationParams()->BackBufferFormat;
    } else {
        format = D3DFMT_UNKNOWN;
    }

    res = pCtx->GetResourceManager()->
        CreateTexture(d3dsdo->width, d3dsdo->height,
                      isRTT, isOpaque,
                      &format, 0/*usage*/, &d3dsdo->pResource);
    if (SUCCEEDED(res)) {
        J2dTraceLn1(J2D_TRACE_VERBOSE,
                    "  created texture pResource=%x", d3dsdo->pResource);
        d3dsdo->pResource->SetSDOps(d3dsdo);
    } else {
        D3DRQ_MarkLostIfNeeded(res, d3dsdo);
    }
    D3DSD_SetNativeDimensions(env, d3dsdo);

    return SUCCEEDED(res);
}

/*
 * Class:     sun_java2d_d3d_D3DSurfaceData
 * Method:    initPlain
 * Signature: (JZ)Z
 */
JNIEXPORT jboolean JNICALL
Java_sun_java2d_d3d_D3DSurfaceData_initRTSurface
  (JNIEnv *env, jobject d3dsd, jlong pData, jboolean isOpaque)
{
    HRESULT res;
    D3DSDOps *d3dsdo;
    D3DContext *pCtx;
    D3DPipelineManager *pMgr;
    D3DFORMAT format = D3DFMT_UNKNOWN;

    J2dTraceLn(J2D_TRACE_INFO, "D3DSurfaceData_initRTSurface");

    RETURN_STATUS_IF_NULL(d3dsdo = (D3DSDOps *)jlong_to_ptr(pData), JNI_FALSE);
    RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), JNI_FALSE);

    if (FAILED(res = pMgr->GetD3DContext(d3dsdo->adapter, &pCtx))) {
        D3DRQ_MarkLostIfNeeded(res, d3dsdo);
        return JNI_FALSE;
    }
    RETURN_STATUS_IF_NULL(pCtx->GetResourceManager(), JNI_FALSE);

    pCtx->GetResourceManager()->ReleaseResource(d3dsdo->pResource);
    d3dsdo->pResource = NULL;

    res = pCtx->GetResourceManager()->
            CreateRTSurface(d3dsdo->width, d3dsdo->height,
                            isOpaque, FALSE /*lockable*/,
                            &format, &d3dsdo->pResource);
    if (SUCCEEDED(res)) {
        J2dTraceLn1(J2D_TRACE_VERBOSE, "  created RT surface pResource=0x%x",
                    d3dsdo->pResource);
        d3dsdo->pResource->SetSDOps(d3dsdo);
    } else {
        D3DRQ_MarkLostIfNeeded(res, d3dsdo);
    }
    D3DSD_SetNativeDimensions(env, d3dsdo);

    return SUCCEEDED(res);
}

/*
 * Class:     sun_java2d_d3d_D3DSurfaceData
 * Method:    initFlipBackbuffer
 * Signature: (JJIZ)Z
 */
JNIEXPORT jboolean
JNICALL Java_sun_java2d_d3d_D3DSurfaceData_initFlipBackbuffer
  (JNIEnv *env, jobject d3dsd, jlong pData, jlong pPeerData,
  jint numBuffers, jint swapEffect,
  jint vSyncType)
{
    HRESULT res;
    D3DSDOps *d3dsdo;
    D3DContext *pCtx;
    D3DPipelineManager *pMgr;
    HWND hWnd;
    UINT presentationInterval;
    AwtComponent *pPeer;
    RECT r = { 0, 0, 0, 0 };

    J2dTraceLn(J2D_TRACE_INFO, "D3DSurfaceData_initFlipBackbuffer");

    RETURN_STATUS_IF_NULL(d3dsdo = (D3DSDOps *)jlong_to_ptr(pData), JNI_FALSE);
    RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), JNI_FALSE);
    RETURN_STATUS_IF_NULL(pPeer = (AwtComponent *)jlong_to_ptr(pPeerData),
                          JNI_FALSE);

    hWnd = pPeer->GetHWnd();
    if (!IsWindow(hWnd)) {
        J2dTraceLn(J2D_TRACE_WARNING,
                   "D3DSurfaceData_initFlipBackbuffer: disposed component");
        return JNI_FALSE;
    }

    pPeer->GetInsets(&r);
    d3dsdo->xoff = -r.left;
    d3dsdo->yoff = -r.top;

    if (FAILED(res = pMgr->GetD3DContext(d3dsdo->adapter, &pCtx))) {
        D3DRQ_MarkLostIfNeeded(res, d3dsdo);
        return JNI_FALSE;
    }
    RETURN_STATUS_IF_NULL(pCtx->GetResourceManager(), JNI_FALSE);

    pCtx->GetResourceManager()->ReleaseResource(d3dsdo->pResource);
    d3dsdo->pResource = NULL;

    d3dsdo->swapEffect = (D3DSWAPEFFECT)swapEffect;

    // in full-screen mode we should v-sync
    if (pCtx->GetPresentationParams()->Windowed) {
        if (vSyncType == VSYNC_ON) {
            presentationInterval = D3DPRESENT_INTERVAL_ONE;
            J2dTraceLn(J2D_TRACE_VERBOSE,
                       "  windowed, forced interval: ONE");
        } else {
            presentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
            J2dTraceLn(J2D_TRACE_VERBOSE,
                       "  windowed, default interval: IMMEDIATE");
        }

        // REMIND: this is a workaround for the current issue
        // we have with non-copy flip chains: since we can not specify
        // the dest rectangle for Present for these modes, the result of
        // Present(NULL, NULL) is scaled to the client area.
        if (d3dsdo->xoff != 0 || d3dsdo->yoff != 0) {
            d3dsdo->swapEffect = D3DSWAPEFFECT_COPY;
        }
    } else {
        if (vSyncType == VSYNC_OFF) {
            presentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
            J2dTraceLn(J2D_TRACE_VERBOSE,
                       "  full-screen, forced interval: IMMEDIATE");
        } else {
            presentationInterval = D3DPRESENT_INTERVAL_ONE;
            J2dTraceLn(J2D_TRACE_VERBOSE,
                       "  full-screen, default interval: ONE");
        }
    }

    res = pCtx->GetResourceManager()->
        CreateSwapChain(hWnd, numBuffers,
                        d3dsdo->width, d3dsdo->height,
                        d3dsdo->swapEffect, presentationInterval,
                        &d3dsdo->pResource);
    if (SUCCEEDED(res)) {
        J2dTraceLn1(J2D_TRACE_VERBOSE, "  created swap chain pResource=0x%x",
                    d3dsdo->pResource);
        d3dsdo->pResource->SetSDOps(d3dsdo);
    } else {
        D3DRQ_MarkLostIfNeeded(res, d3dsdo);
    }
    D3DSD_SetNativeDimensions(env, d3dsdo);

    return SUCCEEDED(res);
}

/*
 * Class:     sun_java2d_d3d_D3DSurfaceData
 * Method:    dbGetPixelNative
 * Signature: (JII)I
 */
JNIEXPORT jint JNICALL Java_sun_java2d_d3d_D3DSurfaceData_dbGetPixelNative
  (JNIEnv *env, jclass clazz, jlong pData, jint x, jint y)
{
    HRESULT res;
    D3DSDOps *d3dsdo;
    D3DContext *pCtx;
    D3DPipelineManager *pMgr;
    D3DResource *pLockableRes;
    jint pixel = 0;

    J2dTraceLn(J2D_TRACE_INFO, "D3DSurfaceData_dbGetPixelNative");

    RETURN_STATUS_IF_NULL(d3dsdo = (D3DSDOps *)jlong_to_ptr(pData), pixel);
    RETURN_STATUS_IF_NULL(d3dsdo->pResource, pixel);
    RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), pixel);

    if (FAILED(res = pMgr->GetD3DContext(d3dsdo->adapter, &pCtx))) {
        D3DRQ_MarkLostIfNeeded(res, d3dsdo);
        return pixel;
    }
    RETURN_STATUS_IF_NULL(pCtx->GetResourceManager(), 0);

    IDirect3DDevice9 *pd3dDevice = pCtx->Get3DDevice();
    IDirect3DSurface9 *pSrc = d3dsdo->pResource->GetSurface();
    D3DFORMAT srcFmt = d3dsdo->pResource->GetDesc()->Format;

    pCtx->UpdateState(STATE_OTHEROP);

    res = pCtx->GetResourceManager()->
            GetLockableRTSurface(1, 1, srcFmt, &pLockableRes);
    if (SUCCEEDED(res)) {
        IDirect3DSurface9 *pTmpSurface;
        RECT srcRect = { x, y, x+1, y+1};
        RECT dstRect = { 0l, 0l, 1, 1 };

        pTmpSurface = pLockableRes->GetSurface();
        res = pd3dDevice->StretchRect(pSrc, &srcRect, pTmpSurface, &dstRect,
                                      D3DTEXF_NONE);
        if (SUCCEEDED(res)) {
            D3DLOCKED_RECT lRect;

            res = pTmpSurface->LockRect(&lRect, &dstRect, D3DLOCK_NOSYSLOCK);
            if (SUCCEEDED(res)) {
                if (srcFmt == D3DFMT_X8R8G8B8) {
                    pixel = *(jint*)lRect.pBits;
                } else {
                    pixel = *(unsigned short*)lRect.pBits;
                }
                pTmpSurface->UnlockRect();
            }
        }
    }
    D3DRQ_MarkLostIfNeeded(res, d3dsdo);

    return pixel;
}

/*
 * Class:     sun_java2d_d3d_D3DSurfaceData
 * Method:    dbSetPixelNative
 * Signature: (JIII)V
 */
JNIEXPORT void JNICALL Java_sun_java2d_d3d_D3DSurfaceData_dbSetPixelNative
  (JNIEnv *env, jclass clazz, jlong pData, jint x, jint y, jint pixel)
{
    HRESULT res;
    D3DSDOps *d3dsdo;
    D3DResource *pLockableRes;
    D3DContext *pCtx;
    D3DPipelineManager *pMgr;

    J2dTraceLn(J2D_TRACE_INFO, "D3DSurfaceData_dbSetPixelNative");

    RETURN_IF_NULL(d3dsdo = (D3DSDOps *)jlong_to_ptr(pData));
    RETURN_IF_NULL(d3dsdo->pResource);
    RETURN_IF_NULL(pMgr = D3DPipelineManager::GetInstance());

    if (FAILED(res = pMgr->GetD3DContext(d3dsdo->adapter, &pCtx))) {
        D3DRQ_MarkLostIfNeeded(res, d3dsdo);
        return;
    }
    RETURN_IF_NULL(pCtx->GetResourceManager());

    IDirect3DDevice9 *pd3dDevice = pCtx->Get3DDevice();
    IDirect3DSurface9 *pSrc = d3dsdo->pResource->GetSurface();
    D3DFORMAT srcFmt = d3dsdo->pResource->GetDesc()->Format;

    pCtx->UpdateState(STATE_OTHEROP);

    res = pCtx->GetResourceManager()->
            GetLockableRTSurface(1, 1, srcFmt, &pLockableRes);
    if (SUCCEEDED(res)) {
        IDirect3DSurface9 *pTmpSurface;
        D3DLOCKED_RECT lRect;
        RECT srcRect = { 0l, 0l, 1, 1 };
        RECT dstRect = { x, y, x+1, y+1};

        pTmpSurface = pLockableRes->GetSurface();
        res = pTmpSurface->LockRect(&lRect, &srcRect, D3DLOCK_NOSYSLOCK);
        if (SUCCEEDED(res)) {
            if (srcFmt == D3DFMT_X8R8G8B8) {
                *(jint*)lRect.pBits = pixel;
            } else {
                *(unsigned short*)lRect.pBits = (unsigned short)pixel;
            }
            pTmpSurface->UnlockRect();

            res = pd3dDevice->StretchRect(pTmpSurface, &srcRect, pSrc, &dstRect,
                                          D3DTEXF_NONE);
        }
    }
    D3DRQ_MarkLostIfNeeded(res, d3dsdo);
}

/*
 * Class:     sun_java2d_d3d_D3DSurfaceData
 * Method:    getNativeResourceNative
 * Signature: (JI)J
 */
JNIEXPORT jlong JNICALL
    Java_sun_java2d_d3d_D3DSurfaceData_getNativeResourceNative
        (JNIEnv *env, jclass d3sdc, jlong pData, jint resType)
{
    D3DSDOps *d3dsdo;

    J2dTraceLn(J2D_TRACE_INFO, "D3DSurfaceData_getNativeResourceNative")

    RETURN_STATUS_IF_NULL(d3dsdo = (D3DSDOps *)jlong_to_ptr(pData), 0L);

    if (resType == D3D_DEVICE_RESOURCE) {
        HRESULT res;
        D3DPipelineManager *pMgr;
        D3DContext *pCtx;

        RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), 0L);
        if (FAILED(res = pMgr->GetD3DContext(d3dsdo->adapter, &pCtx))) {
            D3DRQ_MarkLostIfNeeded(res, d3dsdo);
            return 0L;
        }
        return ptr_to_jlong(pCtx->Get3DDevice());
    }

    RETURN_STATUS_IF_NULL(d3dsdo->pResource, 0L);

    if (resType == RT_PLAIN || resType == RT_TEXTURE) {
        return ptr_to_jlong(d3dsdo->pResource->GetSurface());
    }
    if (resType == TEXTURE) {
        return ptr_to_jlong(d3dsdo->pResource->GetTexture());
    }
    if (resType == FLIP_BACKBUFFER) {
        return ptr_to_jlong(d3dsdo->pResource->GetSwapChain());
    }

    return 0L;
}

/*
 * Class:     sun_java2d_d3d_D3DSurfaceData
 * Method:    updateWindowAccelImpl
 * Signature: (JJII)Z
 */
JNIEXPORT jboolean
JNICALL Java_sun_java2d_d3d_D3DSurfaceData_updateWindowAccelImpl
  (JNIEnv *env, jclass clazz, jlong pd3dsd, jlong pData, jint w, jint h)
{
    HRESULT res;
    AwtWindow *window;
    HBITMAP hBitmap = NULL;
    D3DSDOps *d3dsdo;
    D3DResource *pSrcRes;
    D3DContext *pCtx;
    D3DPipelineManager *pMgr;
    D3DResource *pLockableRes = NULL;
    IDirect3DSurface9 *pTmpSurface = NULL;
    IDirect3DDevice9 *pd3dDevice = NULL;
    D3DLOCKED_RECT lockedRect;

    J2dTraceLn(J2D_TRACE_ERROR, "D3DSurfaceData_updateWindowAccelImpl");

    if (w <= 0 || h <= 0) {
        return JNI_TRUE;
    }

    RETURN_STATUS_IF_NULL(window = (AwtWindow *)jlong_to_ptr(pData), JNI_FALSE);
    RETURN_STATUS_IF_NULL(d3dsdo = (D3DSDOps *)jlong_to_ptr(pd3dsd), JNI_FALSE);
    RETURN_STATUS_IF_NULL(pMgr = D3DPipelineManager::GetInstance(), JNI_FALSE);
    RETURN_STATUS_IF_NULL(pSrcRes = d3dsdo->pResource, JNI_FALSE);

    if (FAILED(res = pMgr->GetD3DContext(d3dsdo->adapter, &pCtx))) {
        D3DRQ_MarkLostIfNeeded(res, d3dsdo);
        return JNI_FALSE;
    }

    RETURN_STATUS_IF_NULL(pd3dDevice = pCtx->Get3DDevice(), JNI_FALSE);
    pCtx->UpdateState(STATE_OTHEROP);

    res = pCtx->GetResourceManager()->
            GetBlitOSPSurface(pSrcRes->GetDesc()->Width,
                              pSrcRes->GetDesc()->Height,
                              pSrcRes->GetDesc()->Format,
                              &pLockableRes);
    if (FAILED(res)) {
        D3DRQ_MarkLostIfNeeded(res, d3dsdo);
        return JNI_FALSE;
    }
    pTmpSurface = pLockableRes->GetSurface();

    res = pd3dDevice->GetRenderTargetData(pSrcRes->GetSurface(), pTmpSurface);
    if (FAILED(res)) {
        D3DRQ_MarkLostIfNeeded(res, d3dsdo);
        return JNI_FALSE;
    }

    res = pTmpSurface->LockRect(&lockedRect, NULL, D3DLOCK_NOSYSLOCK);
    if (SUCCEEDED(res)) {
        hBitmap =
            BitmapUtil::CreateBitmapFromARGBPre(w, h,
                                                lockedRect.Pitch,
                                                (int*)lockedRect.pBits);
        pTmpSurface->UnlockRect();
    }
    RETURN_STATUS_IF_NULL(hBitmap, JNI_FALSE);

    window->UpdateWindow(env, NULL, w, h, hBitmap);

    // hBitmap is released in UpdateWindow

    return JNI_TRUE;
}
}
