javauis/eswt_qt/org.eclipse.swt/Eclipse_SWT_PI/qt/library/graphics/qt/windowsurfaceimpl_symbian.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 04 Oct 2010 00:10:53 +0300
changeset 79 2f468c1958d0
parent 48 e0d6e9bd3ca7
permissions -rw-r--r--
Revision: v2.2.15 Kit: 201039

/*******************************************************************************
 * Copyright (c) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Nokia Corporation - initial API and implementation
 *******************************************************************************/

//#include <QtOpenGL>
#include <QWidget>
#include <QPainter>
#include <QPaintEngine>
#include <fbs.h>
#include <coemain.h>
#include "windowsurfaceimpl_symbian.h"
#include "gfxlog.h"

namespace Java { namespace GFX {

WindowSurfaceImpl::WindowSurfaceImpl(QPaintDevice* aSurface, WindowSurfaceType aType)
: mIsBound(false),
  mBufferedRendering(false),
  mPreserveLocalSurface(false),
  mAutoRefresh(false),
  mPaintingStarted(false)
{
    GFX_LOG_FUNC_CALL();
    mMainSurface.device = aSurface;
    mMainSurface.type = aType;
}

WindowSurfaceImpl::WindowSurfaceImpl(QWidget* aWidget, bool aAutoRefresh)
: mIsBound(false),
  mBufferedRendering(false),
  mPreserveLocalSurface(false),
  mAutoRefresh(false),
  mPaintingStarted(false)
{
    GFX_LOG_FUNC_CALL();
    if(aWidget == NULL) 
    {
        throw GfxException(EGfxErrorIllegalArgument, "Target widget is NULL");
    }
    mMainSurface.widget = aWidget;
    mAutoRefresh = aAutoRefresh;
    updateSurfaceData();   
}


WindowSurfaceImpl::~WindowSurfaceImpl()
{
    GFX_LOG_FUNC_CALL();
    deleteLocalSurface();
}

void WindowSurfaceImpl::beginPaint(int aX, int aY, int aWidth, int aHeight)
{
    if(mAutoRefresh && (mMainSurface.widget != NULL)) 
    {
        updateSurfaceData();
    }
    if(!mMainSurface.localSurfaceInUse)
    {
        QRegion region(aX, aY, aWidth, aHeight);
        mMainSurface.qSurface->beginPaint(region);
        // In case local surface was used last round
        // and we now have Qt's window surface again
        // delete the local surface to save memory,
        if(!mPreserveLocalSurface)
        {    
            deleteLocalSurface();
        }
    }
    mPaintingStarted = true;
}

void WindowSurfaceImpl::endPaint()
{
    if(!mMainSurface.localSurfaceInUse)
    {
        mMainSurface.qSurface->endPaint(QRegion());
    }
    mPaintingStarted = false;
}

void WindowSurfaceImpl::flush()
{
    if(!mMainSurface.localSurfaceInUse)
    { 
        QRegion region(0,0,mMainSurface.device->width(), mMainSurface.device->height());  
        mMainSurface.qSurface->flush(mMainSurface.widget, region, QPoint());
    }
}

void WindowSurfaceImpl::bind(int aCapabilies)
{
    GFX_LOG_FUNC_CALL();
    // Bind is not allowed if beginPaint has not been called
    if(!mPaintingStarted)
    {
        return;
    }
    
    switch (mMainSurface.type)
    {
        case WsTypeQtImage:
        {
            break;
        }
        case WsTypeEglSurface:
        {
            // If caller does not support EGL surface
            // create local surface to be used as target and
            // copy pixels from window surface to local surface
            if ((aCapabilies & WsTypeEglSurface) == 0) {
                if(!isLocalSurfaceValid())
                {
                    createLocalSurface();
                }
                mBufferedRendering = true;
                mPreserveLocalSurface = true;
                // TODO copy pixels from EGL surface to 
                // QImage created here
                
            } else {
                // Caller supports EGL surface, so just 
                // save EGL state here
                saveEglState();
            }
            break;
        }
        case WsTypeSymbianBitmap:
        {
            break;
        }
        default:
        {
            // we should newer get here
        }
    }
    mIsBound = true;
}

int WindowSurfaceImpl::getType()
{
    GFX_LOG_FUNC_CALL();
    if(mBufferedRendering)
    {
        // only supported local buffer is QImage
        return WsTypeQtImage;
    }
    else
    {
        return mMainSurface.type;
    }
}

QPaintDevice* WindowSurfaceImpl::getDevice()
{
    return mMainSurface.device;
}


EGLSurface WindowSurfaceImpl::getEglSurface()
{
    return mEgl.drawSurface;
}

EGLenum WindowSurfaceImpl::getEglApi()
{
    return mEgl.api;
}

CFbsBitmap* WindowSurfaceImpl::getSymbianBitmap()
{
    return mMainSurface.symbianBitmap;
}

QImage* WindowSurfaceImpl::getQtImage()
{
    return mMainSurface.localSurface;
}

void WindowSurfaceImpl::refresh()
{
    updateSurfaceData();
}

void WindowSurfaceImpl::release()
{
    GFX_LOG_FUNC_CALL();
    if (!mIsBound)
    {
        return;
    }

    // this means that we are using localSurface
    // as intermediate buffer for caller due to 
    // lacking support for the actual surface type
    if (mBufferedRendering)
    {
        mPainter.begin(mMainSurface.device);
        mPainter.drawImage(QPoint(0,0),*mMainSurface.localSurface);
        mPainter.end();
        mBufferedRendering = false;
    }
    else
    {
        if(mMainSurface.type == WsTypeEglSurface)
        {
            restoreEglState();
        }
        // pixels are already rendered to the target
        // no post blitter hook needed
    }
    mIsBound = false;
}

void WindowSurfaceImpl::dispose()
{
    GFX_LOG_FUNC_CALL();
    delete this;
}

void WindowSurfaceImpl::handleSymbianWindowVisibilityChange(bool aVisible)
{
    if(mPaintingStarted)
    {
        // TODO window getting invisible in the middle of paint
        return;
    }
    
    if (!aVisible)
    {
        // Switch to sw rendering
        if(!isLocalSurfaceValid()) 
        {
            if(mMainSurface.localSurfaceInUse) 
            {
                deleteLocalSurface();
            }
            
            CFbsBitmap* bitmap = new(ELeave) CFbsBitmap;
            CleanupStack::PushL(bitmap);
            int err = bitmap->Create(TSize(mMainSurface.widget->width(), mMainSurface.widget->height()), 
                CCoeEnv::Static()->ScreenDevice()->DisplayMode());
            eglCopyBuffers(mEgl.display, mEgl.readSurface, bitmap);
            mMainSurface.localSurface = new QImage(QPixmap::fromSymbianCFbsBitmap(bitmap).toImage());
            CleanupStack::Pop(bitmap);
            
            mMainSurface.qSurface = NULL;
            mMainSurface.device = mMainSurface.localSurface;
            mMainSurface.type = WsTypeQtImage;
            mMainSurface.localSurfaceInUse = true;
        }
    }
    
    // Otherwise updateSurfaceData() will switch back to hw rendering
}

void WindowSurfaceImpl::saveEglState()
{
    // Some painter needs to be active on the device
    // in order to get correct data from EGL, so if 
    // there's no active painter, activate one here
    if(!mMainSurface.device->paintingActive())
    {
        mPainter.begin(mMainSurface.device);
    }
    mEgl.drawSurface = eglGetCurrentSurface(EGL_DRAW);
    mEgl.readSurface = eglGetCurrentSurface(EGL_READ);
    mEgl.display = eglGetCurrentDisplay();
    mEgl.context = eglGetCurrentContext();
    mEgl.api = eglQueryAPI();
}
    
void WindowSurfaceImpl::restoreEglState()
{
    eglBindAPI(mEgl.api);
    eglMakeCurrent(mEgl.display, mEgl.drawSurface, mEgl.readSurface, mEgl.context);
    
    // if owned painter was activate to the device 
    // release it
    if(mPainter.isActive())
    {
        mPainter.end();
    }
}

// Private methods

void WindowSurfaceImpl::createLocalSurface()
{
    mMainSurface.localSurface = new QImage(mMainSurface.widget->width(), mMainSurface.widget->height(), QImage::Format_RGB32/*QImage::Format_ARGB32*/);
    if(mMainSurface.localSurface->isNull()) 
    {   
        throw GfxException(EGfxErrorNoMemory, "Local Surface creation failed");
    }   
}

void WindowSurfaceImpl::deleteLocalSurface()
{
    if(mMainSurface.localSurface != NULL)
    {
        delete mMainSurface.localSurface;
        mMainSurface.localSurface = NULL;
    }
    
}

bool WindowSurfaceImpl::isLocalSurfaceValid()
{
    if(mMainSurface.localSurface == NULL)
    {
        return false;
    }
    
    if((mMainSurface.localSurface->width() == mMainSurface.widget->width()) && 
       (mMainSurface.localSurface->height() == mMainSurface.widget->height()))
    {
        return true;
    }
    return false;
}

void WindowSurfaceImpl::updateSurfaceData()
{   
    // If painting is active, i.e. beginPaint has been called 
    // surface data is not updated
    if(mPaintingStarted)
    {
        return;
    }
    QWindowSurface* surface = mMainSurface.widget->windowSurface();
    
    // If window surface is null it means that the widget has been 
    // sent to background and widget's window surface has been deleted, 
    // in such case create own QImage as local surface in order to support 
    // rendering in background
    if(surface == NULL)
    {
        // check if we already have local surface with valid size
        if(!isLocalSurfaceValid()) 
        {
            // incase we have invalid surface delete the current one
            // and create new
            if(mMainSurface.localSurfaceInUse) 
            {
                deleteLocalSurface();
            }
            createLocalSurface();
            // set info
            mMainSurface.qSurface = NULL;
            mMainSurface.device = mMainSurface.localSurface;
            mMainSurface.type = WsTypeQtImage;
            mMainSurface.localSurfaceInUse = true;
            return;
        }
        else 
        {
            // We have valid local surface so make sure its active and return
            mMainSurface.localSurfaceInUse = true;
            return;
        }
    }
    else 
    {
        // We got Qt's window surface, so in case we had local surface in use
        // delete it as it's not used anymore
        if(mMainSurface.localSurfaceInUse)
        {
            // in case we have needed the local surface as temp
            // buffer for a client, it is not deleted as we most likely
            // will need it again. In any case the local surface 
            // stops to be the main surface and is atleast demoted to 
            // temp surface.
            if(!mPreserveLocalSurface)
            {
                deleteLocalSurface();
            }
        }
    }
    
    // We got window surface so extract information
    QPaintDevice* device = surface->paintDevice();
    QPaintEngine* engine = NULL;
    
    // If the device is active it means that some painter is attached to the widget,
    // if not then we attach our own painter to do the job
    if(device->paintingActive())
    {
        engine = device->paintEngine();
    }
    else
    {
        mPainter.begin(device);
        engine = mPainter.paintEngine();
    }
    
    // determine the surface type based on the engine used
    // as Qt does not provide exact info of the surface type
    switch (engine->type())
    {
        case QPaintEngine::OpenVG:
            // surface is EGL window surface
            mMainSurface.type = WsTypeEglSurface;
            break;
        case QPaintEngine::Raster:
            mMainSurface.type = WsTypeSymbianBitmap;
            if(device->devType() == QInternal::Pixmap)
            {
                QPixmap* pixmap = static_cast<QPixmap*>(device);
                mMainSurface.symbianBitmap = pixmap->toSymbianCFbsBitmap();
            }
            else 
            {
                throw GfxException(EGfxErrorIllegalArgument, "Unsupported device type");
            }
            break;
        default:
            throw GfxException(EGfxErrorIllegalArgument, "Unsupported widget window surface type");
    }
    
    // release painter if its active
    if(mPainter.isActive())
    {
        mPainter.end();
    }
    mMainSurface.qSurface = surface;
    mMainSurface.device = device;
    mMainSurface.localSurfaceInUse = false;
}


} // namespace GFX
} // namespace Java