/*
* Copyright (c) 2008 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 "HuiFxVg10Engine.h"
#include "HuiFxVg10OnscreenRenderbuffer.h"
#include "HuiFxVg10OffscreenRenderbuffer.h"
#include "HuiFxVg10BlurFilter.h"
#include "HuiFxVg10BrightnessContrastFilter.h"
#include "HuiFxVg10HSLFilter.h"
#include "HuiFxVg10ColorizeFilter.h"
#include "HuiFxVg10OutlineFilter.h"
#include "HuiFxVg10BevelFilter.h"
#include "HuiVg10Gc.h"
#include "uiacceltk/HuiEnv.h"
#include "uiacceltk/HuiDisplay.h"
#include "uiacceltk/HuiStatic.h"

CHuiFxVg10Engine* CHuiFxVg10Engine::NewL(CHuiVg10RenderPlugin& aPlugin)
    {
    CHuiFxVg10Engine* e = new (ELeave) CHuiFxVg10Engine();
    CleanupStack::PushL(e);
    e->ConstructL(aPlugin);
    CleanupStack::Pop(e);
    return e;
    }

void CHuiFxVg10Engine::ConstructL(CHuiVg10RenderPlugin& aPlugin)
    {
    CHuiFxEngine::ConstructL(EHuiFxEngineVg10);
    iPlugin = &aPlugin;
    iDefaultBuffer = 0;
	// usually we don't have any vg context set at this point, so we cannot yet create the actual paint
    iCompPaint = VG_INVALID_HANDLE; 
    }

CHuiFxVg10Engine::~CHuiFxVg10Engine()
    {
    delete iDefaultBuffer;
    vgDestroyPaint(iCompPaint);
    }

CHuiFxRenderbuffer* CHuiFxVg10Engine::AcquireNativeRenderbuffer(const TSize& aDesiredSize)
    {
    CHuiFxRenderbuffer* ret = NULL;
    TRAP_IGNORE(ret = CHuiFxVg10OffscreenRenderbuffer::NewL(*iPlugin, aDesiredSize));
    return ret;
    }

void CHuiFxVg10Engine::ReleaseNativeRenderbuffer(CHuiFxRenderbuffer* aBuffer)
    {
    ASSERT(aBuffer);
    ASSERT(aBuffer != iDefaultBuffer);
    
    delete aBuffer;
    aBuffer = 0;
    }

CHuiFxRenderbuffer* CHuiFxVg10Engine::DefaultRenderbuffer()
    {
    if (!iDefaultBuffer)
        {
        TRAPD(err, RestoreL());
        if(err != KErrNone)
            {
            return NULL;
            }
        }
    return iDefaultBuffer;
    }

void CHuiFxVg10Engine::Release()
    {
    delete iDefaultBuffer;
    iDefaultBuffer = 0;
    }

void CHuiFxVg10Engine::RestoreL()
    {
    if (!iDefaultBuffer)
        {
        CHuiVg10RenderSurface* surface = static_cast<CHuiVg10RenderSurface*>(CHuiStatic::CurrentRenderSurface());
        if (surface)
            {
            iDefaultBuffer = CHuiFxVg10OnscreenRenderbuffer::NewL(*iPlugin, *surface);
            }
        }
    }

CHuiFxFilter* CHuiFxVg10Engine::CreateFilterL(THuiFxFilterType aType)
    {
    switch (aType)
        {
        case EFilterTypeBrightnessContrast:
            return CHuiFxVg10BrightnessContrastFilter::NewL();
            // no break because we returned already
            
        case EFilterTypeBlur:
            return CHuiFxVg10BlurFilter::NewL();
            // no break because we returned already

        case EFilterTypeHSL:
        case EFilterTypeDesaturate: // desaturate is generated by hsl filter
            return CHuiFxVg10HSLFilter::NewL();
            // no break because we returned already
            
        case EFilterTypeColorize:
            return CHuiFxVg10ColorizeFilter::NewL();
            // no break because we returned already

        case EFilterTypeOutline:
            return CHuiFxVg10OutlineFilter::NewL();
            // no break because we returned already

        case EFilterTypeBevel:
            return CHuiFxVg10BevelFilter::NewL();
            // no break because we returned already

        default: // unsupported
            break;
        }
    return NULL;    
    }

void CHuiFxVg10Engine::Composite(CHuiFxRenderbuffer& aTarget, CHuiFxRenderbuffer& aSource, 
                                      const TRect& aTargetRect, const TRect& aSourceRect,
                                      THuiFxBlendingMode aMode, TInt aAlpha)
    {
    const TInt VG_MATRIX_SIZE = 9;
    CHuiGc& gc = aTarget.BindAsRenderTarget();
    aSource.BindAsTexture(ERenderbufferUsageReadOnly);
        
#if 0 // render debug rectangle
    VGint x = aTargetRect.iTl.iX;
    VGint y = aTarget.Size().iHeight - aTargetRect.iBr.iY;
    VGint w = aTargetRect.Size().iWidth;
    VGint h = aTargetRect.Size().iHeight;
    VGfloat color[] = 
        {
        .2f, .4f, .6f, 1.0f
        };
    vgSetfv(VG_CLEAR_COLOR, 4, color);
    vgClear(x, y, w, h);
    HUIFX_VG_INVARIANT();
#else
    VGImage image = (reinterpret_cast<CHuiFxVg10RenderbufferBase*>(&aSource))->AcquireSubImage(aSourceRect);

    ASSERT(vgGeti(VG_MATRIX_MODE) == VG_MATRIX_IMAGE_USER_TO_SURFACE);
    VGfloat oldMatrix[VG_MATRIX_SIZE];
    vgGetMatrix(oldMatrix);
    vgLoadIdentity();
    vgSeti(VG_SCISSORING, VG_FALSE); 
    VGint blendMode = vgGeti(VG_BLEND_MODE);

    // Choose a blending mode
    switch (aMode)
        {
    case EBlendingModeReplace:
        vgSeti(VG_BLEND_MODE, VG_BLEND_SRC);
        break;
    case EBlendingModeOver:
        vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER);
        break;
    case EBlendingModeMultiply:
        vgSeti(VG_BLEND_MODE, VG_BLEND_MULTIPLY);
        break;
    case EBlendingModeAdditive:
        vgSeti(VG_BLEND_MODE, VG_BLEND_ADDITIVE);
        break;
    case EBlendingModeLighten:
        vgSeti(VG_BLEND_MODE, VG_BLEND_LIGHTEN);
        break;
    case EBlendingModeDarken:
        vgSeti(VG_BLEND_MODE, VG_BLEND_DARKEN);
        break;
    default:
        ASSERT(0);
        }
    HUIFX_VG_INVARIANT();
    
    // Update alpha
    TBool mustRestorePaint = EFalse;
    VGPaint userPaint = VG_INVALID_HANDLE;

    if (aAlpha < 0xff)
        {
        if ( iCompPaint == VG_INVALID_HANDLE )
            {
            iCompPaint = vgCreatePaint();
            }
        if ( iCompPaint != VG_INVALID_HANDLE )
            {
            mustRestorePaint = ETrue;
            userPaint = vgGetPaint( VG_FILL_PATH );
            // if the original paint is not set, we'll get an error that must be cleared
            vgSetPaint(iCompPaint, VG_FILL_PATH);
            }
        vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_MULTIPLY);
        vgSetColor(iCompPaint, 0xffffff00 | aAlpha);
        }
    else
        {
        vgSeti(VG_IMAGE_MODE, VG_DRAW_IMAGE_NORMAL);
        }
    
    // we've taken childimage from source so no clipping or adjusting is needed 
    // by source rectangle.
    if (aTarget.BufferType() == EBufferTypeOnscreen)
        {
		// we need to adjust our coordinates to screen coordinates. Hitchcock 
        // thinks origo is on top-left of screen. VG thinks origo is on 
        // bottom-left.
        // We must take orientation if we access vg matrixes directly..
        CHuiDisplay* display = &CHuiStatic::Env().PrimaryDisplay(); // TODO: should use CHuiEnv::CurrentDisplay() ? 
        
        if (display->Orientation() == CHuiGc::EOrientationCCW90)
            {
            // Rotate around origo and move back to displayarea
            vgRotate(-90);
            vgTranslate(aTargetRect.iTl.iX - aTarget.Size().iHeight , aTarget.Size().iWidth - aTargetRect.iBr.iY);
            }
        else if (display->Orientation() == CHuiGc::EOrientationCW90)
            {
            // Rotate around origo and move back to displayarea
            vgRotate(90);
            vgTranslate(aTargetRect.iTl.iX , - aTargetRect.iBr.iY);
            }
        else if (display->Orientation() == CHuiGc::EOrientation180)
            {
            // Rotate around origo and move back to displayarea
            vgRotate(180);
            vgTranslate(aTarget.Size().iWidth- aTargetRect.iTl.iX , - aTargetRect.iBr.iY);
            }
        else
            {
            vgTranslate(aTargetRect.iTl.iX, aTarget.Size().iHeight - aTargetRect.iBr.iY);
            }        
        
        }
    else 
        {
        // offscreen renderbuffer --- we use VG coordinates
        vgTranslate(aTargetRect.iTl.iX, aTargetRect.iTl.iY);
        }
    
    // slowpath
    if(aTargetRect.Size() != aSourceRect.Size())
        {
        VGfloat scaleX = (VGfloat)aTargetRect.Width() / aSourceRect.Width();
        VGfloat scaleY = (VGfloat)aTargetRect.Height() / aSourceRect.Height();
        vgScale(scaleX, scaleY);
        }
    
    vgDrawImage(image);
    
    // Restore default VG state
    vgSeti(VG_SCISSORING, VG_TRUE);     
//    vgSeti(VG_BLEND_MODE, VG_BLEND_SRC_OVER);
    vgSeti(VG_BLEND_MODE, blendMode);
    vgLoadMatrix(oldMatrix);
    HUIFX_VG_INVARIANT();
    if ( mustRestorePaint )
        {
        vgSetPaint(userPaint, VG_FILL_PATH);
        // if the original handle was invalid, this may produce an error
        }
    reinterpret_cast<CHuiFxVg10RenderbufferBase*>(&aSource)->ReleaseSubImage(image);
#endif // debug rectangle
    aSource.UnbindAsTexture();
    aTarget.UnbindAsRenderTarget();
    }

void CHuiFxVg10Engine::Composite(CHuiGc& aGc, CHuiFxRenderbuffer& aSource, const TPoint& aTargetPoint,TBool aOpaque, TInt aAlpha)
    {
    // Directly to screen, overrides onscreen buffer. Always "over" blending mode, constant alpha.
    const CHuiFxVg10OffscreenRenderbuffer* vg10RenderBuffer = (const CHuiFxVg10OffscreenRenderbuffer*) &aSource;        
    CHuiVg10Gc* vg10Gc = (CHuiVg10Gc*)&aGc;
    if (vg10RenderBuffer->Image())
        {
        // For some reason color is weird at this point, so set our own.
        TRgb oldPencolor = aGc.PenColor();
        TInt oldPenAlpha = aGc.PenAlpha();
        
        aGc.SetPenColor(KRgbWhite);
        aGc.SetPenAlpha(aAlpha);
        vg10Gc->UpdateColor();

        // Push matrix
        aGc.Push(EHuiGcMatrixModel);
        
        // Take into account the screen relative position of the buffer
        aGc.Translate(EHuiGcMatrixModel, aTargetPoint.iX, aTargetPoint.iY, 0);

        // Flip the content (because of hitchcock/openvg coordinate differencies       
        aGc.Translate(EHuiGcMatrixModel, 0.0f, aSource.Size().iHeight, 0.0f);
        aGc.Scale(EHuiGcMatrixModel, 1.0f, -1.0f, 1.0f);

        // Do the drawing, handle opaque windows with writealpha, otherwise blend
		if (aOpaque)
			{
			aGc.Disable(CHuiGc::EFeatureBlending);
			}
		else
			{
            aGc.Enable(CHuiGc::EFeatureBlending);
			}
        vgDrawImage(vg10RenderBuffer->Image());

        // Restore pen color, matrix
        aGc.Enable(CHuiGc::EFeatureBlending);        
        aGc.SetPenColor(oldPencolor);                
        aGc.SetPenAlpha(oldPenAlpha);
        aGc.Pop(EHuiGcMatrixModel);
        }
    }
