/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
#include "javax_microedition_m3g_Graphics3D.h"

/*
 * Must be executed in UI thread
 */
JNIEXPORT jboolean JNICALL Java_javax_microedition_m3g_Graphics3D__1isProperRenderer
(JNIEnv* /*aEnv*/, jclass)
{
    EGLContext ctx;
    EGLConfig config;
    EGLSurface surf;
    EGLint attrib[5];
    EGLint numConfigs;
    bool isProperRenderer;

    // initialize EGL and create a temporary surface & context for reading
    // the renderer string
    eglInitialize(eglGetDisplay(EGL_DEFAULT_DISPLAY), NULL, NULL);

    attrib[0] = EGL_SURFACE_TYPE;
    attrib[1] = EGL_PBUFFER_BIT;
    attrib[2] = EGL_NONE;

    eglChooseConfig(eglGetDisplay(0), attrib, &config, 1, &numConfigs);

    ctx = eglCreateContext(eglGetDisplay(0), config, NULL, NULL);

    attrib[0] = EGL_WIDTH;
    attrib[1] = 2;
    attrib[2] = EGL_HEIGHT;
    attrib[3] = 2;
    attrib[4] = EGL_NONE;

    surf = eglCreatePbufferSurface(eglGetDisplay(0), config, attrib);
    eglMakeCurrent(eglGetDisplay(0), surf, surf, ctx);

    // We check if proper renderer is used and return value which is stored
    // on java side and passed to fuctions where is decided if m3g renders
    // into mutable off-screen image or into framebuffer (see
    // Java_javax_microedition_m3g_Graphics3D__1bindGraphics and
    // releaseGraphicsTarget).
    const GLubyte *info;
    info = glGetString(GL_RENDERER);   // get the renderer string

    // check if "MBX" substring is found
    if ( !info ||  strstr((const char *)info, "MBX"))
    {
        // HW renderer detected.
        // If "MBX" HW is detected we must reset alpha for mutable off-screen
        // images by hand (see releaseGraphicsTarget).
        isProperRenderer = false;
    }
    else
    {
        // Other renderers can use m3g core API m3gSetAlphaWrite without
        // performance drop.
        isProperRenderer = true;
    }

    // destroy the temporary surface & context
    eglMakeCurrent(eglGetDisplay(0), NULL, NULL, NULL);
    eglDestroySurface(eglGetDisplay(0), surf);
    eglDestroyContext(eglGetDisplay(0), ctx);

    return isProperRenderer;
}

/*
 * Must be executed in UI thread
 */
JNIEXPORT jboolean JNICALL Java_javax_microedition_m3g_Graphics3D__1bindGraphics
(JNIEnv* aEnv, jclass, jint aCtx,
 jint aSurfaceHandle, jint aClipX, jint aClipY, jint aClipW, jint aClipH,
 jboolean aDepth, jint aHintBits, jboolean aIsProperRenderer)
{
    M3GRenderContext ctx = (M3GRenderContext)aCtx;

    // Fetch the native peer of our target object
    Java::GFX::WindowSurface* wsurf = reinterpret_cast<Java::GFX::WindowSurface*>(aSurfaceHandle);
    
    wsurf->bind(Java::GFX::WsTypeQtImage | Java::GFX::WsTypeEglSurface);
    jboolean isImageTarget = false; /*cmidGraphics->IsImageTarget();*/

    M3G_DO_LOCK

    /*
    * Get the physical screen size and pass it to m3gcore. This affects (improves) the performance
    * as the canvas frambuffer (rendering target) is larger than the physical screen size in
    * devices that have more than one screen orientation, causing extra copying operations.
    *
    * This will improve m3g performance and suppresses extra bitmap copying.
    */

    //TRect screenRect = CCoeEnv::Static()->ScreenDevice()->SizeInPixels();

    eglWaitNative(EGL_CORE_NATIVE_ENGINE);

    if (m3gSetRenderBuffers((M3GRenderContext)aCtx, aDepth ?
                            M3G_COLOR_BUFFER_BIT|M3G_DEPTH_BUFFER_BIT :
                            M3G_COLOR_BUFFER_BIT) && m3gSetRenderHints((M3GRenderContext)aCtx, aHintBits))
    {

        switch (wsurf->getType())
        {
        case Java::GFX::WsTypeQtImage:
        {
            QImage* bitmap = wsurf->getQtImage();
            M3GPixelFormat format  = mapQtPixelformat(bitmap->format());
            m3gBindMemoryTarget((M3GRenderContext)aCtx, bitmap->bits(), (M3Guint)bitmap->width(), (M3Guint)bitmap->height(), format, (M3Guint)(bitmap->width() * 4), NULL);
            break;
        }
        case Java::GFX::WsTypeEglSurface:
        {
            if( eglQueryAPI() != EGL_OPENGL_ES_API )
            {
                eglMakeCurrent( EGL_DEFAULT_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT );
                eglBindAPI( EGL_OPENGL_ES_API );
            }
            m3gBindEGLSurfaceTarget((M3GRenderContext)aCtx, wsurf->getEglSurface() );
            break;
        }
        default:
            M3G_RAISE_EXCEPTION(aEnv, "java/lang/IllegalArgumentException");
            break;
        }

        // Pass the physical screen size to m3gcore
        //m3gSetDisplayArea(aCtx, screenRect.Width(), screenRect.Height());

        m3gSetClipRect((M3GRenderContext)aCtx, aClipX, aClipY, aClipW, aClipH);
        m3gSetViewport((M3GRenderContext)aCtx, aClipX, aClipY, aClipW, aClipH);
    }

    M3G_DO_UNLOCK(aEnv)

    if (isImageTarget && aIsProperRenderer)
    {
        m3gSetAlphaWrite(ctx, M3G_FALSE);
    }

    return isImageTarget;
}

JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1setViewport
(JNIEnv* aEnv, jclass, jint aHContext, jint aX, jint aY,
 jint aWidth, jint aHeight)
{
    M3G_DO_LOCK
    m3gSetViewport((M3GRenderContext)aHContext, aX, aY, aWidth, aHeight);
    M3G_DO_UNLOCK(aEnv)
}


static void releaseTarget(M3GRenderContext aCtx)
{
    eglWaitGL();
    m3gReleaseTarget(aCtx);
}


/*
static void releaseGraphicsTarget(M3GRenderContext aCtx, CMIDGraphics *aGraphics,
    TBool aIsImageTarget, TBool aIsProperRenderer )
    {
    releaseTarget(aCtx);

    // clear alpha for only mutable off-screen images (not for canvas/GameCanvas
    // framebuffer) those images are indetified by aIsImageTarget argument
    if (aIsImageTarget)
        {
        if ( aIsProperRenderer )
            {
            m3gSetAlphaWrite(aCtx, M3G_TRUE);
            }
        else
            {
            CFbsBitmap *bitmap = aGraphics->Bitmap();

            const TInt width = bitmap->SizeInPixels().iWidth;
            const TInt height = bitmap->SizeInPixels().iHeight;
            TInt stride = bitmap->ScanLineLength(width, bitmap->DisplayMode());

            bitmap->LockHeap();

            for (TInt i = 0; i < height; i++)
                {
                const void *srcAddr =
                    ((const char *) bitmap->DataAddress()) + i * stride;
                unsigned char *src = (unsigned char *) srcAddr;
                TInt count = width;
                while (count--)
                    {
                    src += 3; //jump to last byte - alpha channel
                    //setting FF to alpha channel for non-canvas image targets
                    *src |= 0xff;
                    src++;
                    }
                }

            bitmap->UnlockHeap();
            }
        }
    }
*/
JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1releaseGraphics
(JNIEnv* aEnv, jclass, jint aHandle,
 jint aSurfaceHandle, jboolean /*aIsImageTarget*/, jboolean /*aIsProperRenderer*/)
{
    M3G_DO_LOCK

    releaseTarget((M3GRenderContext)aHandle);

    // Release used target surface
    Java::GFX::WindowSurface* surf = reinterpret_cast<Java::GFX::WindowSurface*>(aSurfaceHandle);
    surf->release();
    
    /*
    CMIDGraphics *cmidGraphics = MIDUnhandObject<CMIDGraphics>(aGraphicsHandle);

    CJavaM3GEventSource* eventSource =
        JavaUnhand<CJavaM3GEventSource>(aEventSourceHandle);
    eventSource->ExecuteV(&releaseGraphicsTarget, ((M3GRenderContext) aHandle),
        cmidGraphics, ((TBool) aIsImageTarget), ((TBool) aIsProperRenderer) );
        */
    M3G_DO_UNLOCK(aEnv)
}

JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1setCamera
(JNIEnv* aEnv, jclass, jint aHContext, jint aHCamera, jbyteArray aTransform)
{
    M3GMatrix *transform = NULL;
    if (aTransform)
    {
        transform = (Matrix *)(aEnv->GetByteArrayElements(aTransform, NULL));
        if (transform == NULL)
        {
            M3G_RAISE_EXCEPTION(aEnv, "java/lang/OutOfMemoryError");
            return;
        }
    }

    M3G_DO_LOCK
    m3gSetCamera((M3GRenderContext) aHContext, (M3GCamera) aHCamera, transform);
    M3G_DO_UNLOCK(aEnv)

    if (transform)
    {
        aEnv->ReleaseByteArrayElements(aTransform, (jbyte*)transform, JNI_ABORT);
    }
}

/*
static void renderWorld(M3GRenderContext aHContext,
                        M3GWorld aHWorld)
{
    m3gRenderWorld(aHContext, aHWorld);
}
*/

JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1renderWorld
(JNIEnv* aEnv, jclass, jint aHContext, jint aHWorld)
{
    M3G_DO_LOCK

    m3gRenderWorld((M3GRenderContext) aHContext, (M3GWorld) aHWorld);

    /*
    CJavaM3GEventSource* eventSource = JavaUnhand<CJavaM3GEventSource>(aEventSourceHandle);
    eventSource->ExecuteV(&renderWorld,
                       (M3GRenderContext) aHContext,
                       (M3GWorld) aHWorld);
    */
    M3G_DO_UNLOCK(aEnv)
}

JNIEXPORT jint JNICALL Java_javax_microedition_m3g_Graphics3D__1ctor
(JNIEnv* aEnv, jclass, jint aM3g)
{
    M3G_DO_LOCK
    M3GRenderContext ctx = m3gCreateContext((M3GInterface)aM3g);
    M3G_DO_UNLOCK(aEnv)

    return (jint)ctx;
}

struct RenderStruct
{
    M3GVertexBuffer hVertices;
    M3GIndexBuffer hIndices;
    M3GAppearance hAppearance;
    const M3GMatrix *transform;
};

/*
static void renderImmediate(M3GRenderContext aHContext, RenderStruct *aR, jint aScope)
{
    m3gRender(aHContext,
              aR->hVertices,
              aR->hIndices,
              aR->hAppearance,
              aR->transform, 1.0f, aScope);
}
*/

/*
 * Must be executed in UI thread
 */

JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1render
(JNIEnv* aEnv, jclass, jint aHContext,
 jint aHVertices, jint aHIndices, jint aHAppearance, jbyteArray aTransform, jint aScope)
{
    M3GMatrix *transform = NULL;
    if (aTransform)
    {
        transform = (M3GMatrix *)aEnv->GetByteArrayElements(aTransform, NULL);
        if (transform == NULL)
        {
            M3G_RAISE_EXCEPTION(aEnv, "java/lang/OutOfMemoryError");
            return;
        }
    }

    /*
    RenderStruct r;
    r.hVertices = (M3GVertexBuffer) aHVertices;
    r.hIndices = (M3GIndexBuffer) aHIndices;
    r.hAppearance = (M3GAppearance) aHAppearance;
    r.transform = transform;
    */


    M3G_DO_LOCK

    m3gRender((M3GRenderContext) aHContext, (M3GVertexBuffer) aHVertices, (M3GIndexBuffer) aHIndices, (M3GAppearance) aHAppearance,
              transform, 1.0f, aScope);

    /*
      CJavaM3GEventSource* eventSource = JavaUnhand<CJavaM3GEventSource>(aEventSourceHandle);
    eventSource->ExecuteV(&renderImmediate, ((M3GRenderContext) aHContext), &r, aScope);
    */
    M3G_DO_UNLOCK(aEnv)


    if (transform)
    {
        aEnv->ReleaseByteArrayElements(aTransform, (jbyte*)transform, JNI_ABORT);
    }
}

/*
static void clear(M3GRenderContext aHContext, M3GBackground aHBackground)
{
    m3gClear(aHContext, aHBackground);
}
*/

/*
 * Must be executed in UI thread
 */
JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1clear
(JNIEnv* aEnv, jclass, jint aCtx, jint aBg)
{
    M3G_DO_LOCK
    m3gClear((M3GRenderContext)aCtx, (M3GBackground)aBg);

    /*
    CJavaM3GEventSource* eventSource = JavaUnhand<CJavaM3GEventSource>(aEventSourceHandle);
    eventSource->ExecuteV(&clear, (M3GRenderContext)aCtx, (M3GBackground)aBg);
    */
    M3G_DO_UNLOCK(aEnv)
}

JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1releaseImage
(JNIEnv* aEnv, jclass, jint aHCtx)
{
    M3G_DO_LOCK

    releaseTarget((M3GRenderContext)aHCtx);

    /*
    CJavaM3GEventSource* eventSource = JavaUnhand<CJavaM3GEventSource>(aEventSourceHandle);
    eventSource->ExecuteV(&releaseTarget, (M3GRenderContext)aHCtx);
    */
    M3G_DO_UNLOCK(aEnv)
}

JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1addRef
(JNIEnv* aEnv, jclass, jint aObject)
{
    M3G_DO_LOCK
    m3gAddRef((M3GObject) aObject);
    M3G_DO_UNLOCK(aEnv)
}

JNIEXPORT jint JNICALL Java_javax_microedition_m3g_Graphics3D__1addLight
(JNIEnv* aEnv, jclass, jint aHContext, jint aHLight, jbyteArray aTransform)
{
    M3GMatrix *transform = NULL;
    if (aTransform)
    {
        transform = (M3GMatrix *)(aEnv->GetByteArrayElements(aTransform, NULL));
        if (transform == NULL)
        {
            M3G_RAISE_EXCEPTION(aEnv, "java/lang/OutOfMemoryError");
            return 0;
        }
    }
    M3G_DO_LOCK
    int idx = m3gAddLight((M3GRenderContext) aHContext, (M3GLight) aHLight, transform);
    M3G_DO_UNLOCK(aEnv)

    if (transform)
    {
        aEnv->ReleaseByteArrayElements(aTransform, (jbyte*)transform, JNI_ABORT);
    }

    return idx;
}

/*
static void bindImage(M3GRenderContext hCtx, M3GImage hImg, M3Gbool depth, M3Gbitmask hintBits)
{
    if (m3gSetRenderBuffers(hCtx, depth ? M3G_COLOR_BUFFER_BIT|M3G_DEPTH_BUFFER_BIT : M3G_COLOR_BUFFER_BIT) && m3gSetRenderHints(hCtx, hintBits)) {
        m3gBindImageTarget(hCtx, hImg);
    }
}
*/

JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1bindImage
(JNIEnv* aEnv, jclass, jint aHCtx, jint aImageHandle, jboolean aDepth, jint aHintBits)
{
    M3G_DO_LOCK

    if (m3gSetRenderBuffers((M3GRenderContext)aHCtx, (M3Gbool)aDepth ? M3G_COLOR_BUFFER_BIT|M3G_DEPTH_BUFFER_BIT : M3G_COLOR_BUFFER_BIT) && m3gSetRenderHints((M3GRenderContext)aHCtx, (M3Gbitmask)aHintBits))
    {
        m3gBindImageTarget((M3GRenderContext)aHCtx, (M3GImage)aImageHandle);
    }

    //CJavaM3GEventSource* eventSource = JavaUnhand<CJavaM3GEventSource>(aEventSourceHandle);
    //eventSource->ExecuteV(&bindImage, (M3GRenderContext)aHCtx, (M3GImage)aImageHandle, (M3Gbool)aDepth, (M3Gbitmask)aHintBits);
    M3G_DO_UNLOCK(aEnv)
}

JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1resetLights
(JNIEnv* aEnv, jclass, jint aHContext)
{
    M3G_DO_LOCK
    m3gClearLights((M3GRenderContext) aHContext);
    M3G_DO_UNLOCK(aEnv)
}

JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1setDepthRange
(JNIEnv* aEnv, jclass, jint aHContext, jfloat aDepthNear, jfloat aDepthFar)
{
    M3G_DO_LOCK
    m3gSetDepthRange((M3GRenderContext) aHContext, aDepthNear, aDepthFar);
    M3G_DO_UNLOCK(aEnv)
}

JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1setLight
(JNIEnv* aEnv, jclass, jint aHContext, jint aLightIndex, jint aHLight, jbyteArray aTransform)
{
    M3GMatrix *transform = NULL;
    if (aTransform)
    {
        transform = (M3GMatrix *)(aEnv->GetByteArrayElements(aTransform, NULL));
        if (transform == NULL)
        {
            M3G_RAISE_EXCEPTION(aEnv, "java/lang/OutOfMemoryError");
            return;
        }
    }

    M3G_DO_LOCK
    m3gSetLight((M3GRenderContext) aHContext, aLightIndex, (M3GLight) aHLight, transform);
    M3G_DO_UNLOCK(aEnv)

    if (transform)
    {
        aEnv->ReleaseByteArrayElements(aTransform, (jbyte*)transform, JNI_ABORT);
    }
}

/*
static void renderNode(M3GRenderContext aHCtx,
                       M3GNode aHNode,
                       const M3GMatrix *aMtx)
{
    m3gRenderNode(aHCtx, aHNode, aMtx);
}
*/

JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1renderNode
(JNIEnv* aEnv, jclass, jint aHCtx, jint aHNode, jbyteArray aTransform)
{
    M3GMatrix *transform = NULL;
    if (aTransform)
    {
        transform = (M3GMatrix *)(aEnv->GetByteArrayElements(aTransform, NULL));
        if (transform == NULL)
        {
            M3G_RAISE_EXCEPTION(aEnv, "java/lang/OutOfMemoryError");
            return;
        }
    }

    M3G_DO_LOCK

    m3gRenderNode((M3GRenderContext)aHCtx, (M3GNode)aHNode, (const M3GMatrix *)transform);

    //CJavaM3GEventSource* eventSource = JavaUnhand<CJavaM3GEventSource>(aEventSourceHandle);
    //eventSource->ExecuteV(&renderNode, (M3GRenderContext)aHCtx, (M3GNode)aHNode, (const M3GMatrix *)transform);
    M3G_DO_UNLOCK(aEnv)

    if (aTransform)
    {
        aEnv->ReleaseByteArrayElements(aTransform, (jbyte*)transform, JNI_ABORT);
    }
}

#if defined(M3G_ENABLE_PROFILING)

JNIEXPORT jint JNICALL Java_javax_microedition_m3g_Graphics3D__1getStatistics
(JNIEnv* aEnv, jclass, jintArray aStatArray)
{
    const M3Gint *statArray = (M3Gint *)(aStatArray != NULL ? aEnv->GetIntArrayElements(aStatArray, NULL) : NULL);
    jint statArrayLength = aStatArray ? aEnv->GetArrayLength(aStatArray) : 0;

    if (statArray != NULL && statArrayLength >= sizeof(m3gs_statistic))
    {
        m3gCopy((void*)statArray, m3gs_statistic, sizeof(m3gs_statistic));
    }

    M3G_DO_LOCK
    m3gZero(m3gs_statistic, sizeof(m3gs_statistic));
    M3G_DO_UNLOCK(aEnv)

    if (statArray)
    {
        aEnv->ReleaseIntArrayElements(aStatArray, (jint*)statArray, 0);
    }

    return sizeof(m3gs_statistic);
}

#endif /* M3G_ENABLE_PROFILING */



/* M3G 1.1 JNI Calls */

JNIEXPORT void JNICALL Java_javax_microedition_m3g_Graphics3D__1getViewTransform
(JNIEnv* aEnv, jclass, jint aHCtx, jbyteArray aTransform)
{
    M3GMatrix *transform = NULL;
    if (aTransform)
    {
        transform = (M3GMatrix *)(aEnv->GetByteArrayElements(aTransform, NULL));
        if (transform == NULL)
        {
            M3G_RAISE_EXCEPTION(aEnv, "java/lang/OutOfMemoryError");
            return;
        }
    }

    M3G_DO_LOCK
    m3gGetViewTransform((M3GRenderContext) aHCtx, transform);
    M3G_DO_UNLOCK(aEnv)

    if (transform)
    {
        /* copy array to Java side and release arrays */
        aEnv->ReleaseByteArrayElements(aTransform, (jbyte*)transform, 0);
    }
}

JNIEXPORT jint JNICALL Java_javax_microedition_m3g_Graphics3D__1getCamera
(JNIEnv* aEnv, jclass, jint aHCtx)
{
    M3G_DO_LOCK
    jint camera = (jint)m3gGetCamera((M3GRenderContext)aHCtx);
    M3G_DO_UNLOCK(aEnv)

    return camera;
}

JNIEXPORT jint JNICALL Java_javax_microedition_m3g_Graphics3D__1getLightTransform
(JNIEnv* aEnv, jclass, jint aHCtx, jint aLightIndex, jbyteArray aTransform)
{
    M3GMatrix *transform = NULL;
    if (aTransform)
    {
        transform = (M3GMatrix *)(aEnv->GetByteArrayElements(aTransform, NULL));
        if (transform == NULL)
        {
            M3G_RAISE_EXCEPTION(aEnv, "java/lang/OutOfMemoryError");
            return 0;
        }
    }
    M3G_DO_LOCK
    int lightTransform = (M3Guint)m3gGetLightTransform((M3GRenderContext)aHCtx, aLightIndex, transform);
    M3G_DO_UNLOCK(aEnv)

    if (transform)
    {
        aEnv->ReleaseByteArrayElements(aTransform, (jbyte*)transform, 0);
    }

    return (jint)lightTransform;
}

JNIEXPORT jint JNICALL Java_javax_microedition_m3g_Graphics3D__1getLightCount
(JNIEnv* aEnv, jclass, jint aHCtx)
{
    M3G_DO_LOCK
    jint lightCount = (jint)m3gGetLightCount((M3GRenderContext)aHCtx);
    M3G_DO_UNLOCK(aEnv)

    return lightCount;
}

JNIEXPORT jfloat JNICALL Java_javax_microedition_m3g_Graphics3D__1getDepthRangeNear
(JNIEnv* aEnv, jclass, jint aHCtx)
{
    float depthNear = 0;
    float depthFar = 0;

    M3G_DO_LOCK
    m3gGetDepthRange((M3GRenderContext) aHCtx, &depthNear, &depthFar);
    M3G_DO_UNLOCK(aEnv)

    return (jfloat)depthNear;
}

JNIEXPORT jfloat JNICALL Java_javax_microedition_m3g_Graphics3D__1getDepthRangeFar
(JNIEnv* aEnv, jclass, jint aHCtx)
{
    float depthNear = 0;
    float depthFar = 0;

    M3G_DO_LOCK
    m3gGetDepthRange((M3GRenderContext) aHCtx, &depthNear, &depthFar);
    M3G_DO_UNLOCK(aEnv)

    return (jfloat)depthFar;
}

JNIEXPORT jint JNICALL Java_javax_microedition_m3g_Graphics3D__1getViewportX
(JNIEnv* aEnv, jclass, jint aHCtx)
{
    int viewport[4];

    M3G_DO_LOCK
    m3gGetViewport((M3GRenderContext)aHCtx, &viewport[0],
                   &viewport[1],
                   &viewport[2],
                   &viewport[3]);
    M3G_DO_UNLOCK(aEnv)

    return (jint)viewport[0];
}

JNIEXPORT jint JNICALL Java_javax_microedition_m3g_Graphics3D__1getViewportY
(JNIEnv* aEnv, jclass, jint aHCtx)
{
    int viewport[4];

    M3G_DO_LOCK
    m3gGetViewport((M3GRenderContext)aHCtx, &viewport[0],
                   &viewport[1],
                   &viewport[2],
                   &viewport[3]);
    M3G_DO_UNLOCK(aEnv)

    return (jint)viewport[1];
}

JNIEXPORT jint JNICALL Java_javax_microedition_m3g_Graphics3D__1getViewportWidth
(JNIEnv* aEnv, jclass, jint aHCtx)
{
    int viewport[4];

    M3G_DO_LOCK
    m3gGetViewport((M3GRenderContext)aHCtx, &viewport[0],
                   &viewport[1],
                   &viewport[2],
                   &viewport[3]);
    M3G_DO_UNLOCK(aEnv)

    return (jint)viewport[2];
}

JNIEXPORT jint JNICALL Java_javax_microedition_m3g_Graphics3D__1getViewportHeight
(JNIEnv* aEnv, jclass, jint aHCtx)
{
    int viewport[4];

    M3G_DO_LOCK
    m3gGetViewport((M3GRenderContext)aHCtx, &viewport[0],
                   &viewport[1],
                   &viewport[2],
                   &viewport[3]);
    M3G_DO_UNLOCK(aEnv)

    return (jint)viewport[3];
}

JNIEXPORT jboolean JNICALL Java_javax_microedition_m3g_Graphics3D__1isAASupported
(JNIEnv* /*aEnv*/, jclass, jint aM3g)
{
    M3Gbool aaSupport = M3G_FALSE;

    aaSupport = m3gIsAntialiasingSupported((M3GInterface)aM3g);

    return (jboolean)aaSupport;
}
