/*
* Copyright (c) 2006-2007 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: Implementation for CHuiGc (graphics context) class.
*
*/
#include "uiacceltk/HuiGc.h" // Class definition
#include "HuiRenderPlugin.h"
#include "uiacceltk/HuiSegmentedTexture.h"
#include "uiacceltk/HuiTextMesh.h"
#include "uiacceltk/HuiFont.h"
#include "uiacceltk/HuiImage.h"
#include "uiacceltk/HuiTransformation.h"
#include "uiacceltk/HuiPanic.h"
#include "uiacceltk/HuiUtil.h" // For hui assert.
#include "uiacceltk/HuiTransformation.h"
#include <e32math.h>
#include <AknUtils.h>
const TInt KHuiMaxRecycledRegionCount = 20;
/** Default offset from the Z=0 plane (which 2D projection uses) to the
eyepoint, in 3D projection mode. */
LOCAL_D const TReal32 KDefaultOffset2D = 1199.0;
EXPORT_C CHuiGc::CHuiGc()
: iOrientation(EOrientationNormal),
iNearClipDistance(20.f),
iFarClipDistance(10000.f),
iFovFactor(1.f),
iPenColor(TRgb(255, 255, 255)), iPenAlpha(255),
iAlignHorizontal(EHuiAlignHLeft), iAlignVertical(EHuiAlignVTop),
iFrustumOffsetCompleteness(1.f),
iQuality(EHuiQualityAccurate),
iClipRegionStackCount(0)
{
/// @todo Fix this: The real default is 1.f, but the application should
/// be able to adjust the completeness. Wowidle/Mmenu have been
/// developed with .666f.
iFrustumOffsetCompleteness = .666f;
}
EXPORT_C CHuiGc::~CHuiGc()
{
iTempRegion.Close();
iClipRegion.Close();
iClipRegionStack.Reset();
iRecycledRegions.ResetAndDestroy();
iWindowTransform = NULL;
iCurrentFont = NULL;
}
EXPORT_C void CHuiGc::SetQuality(THuiQuality aQuality)
{
iQuality = aQuality;
}
EXPORT_C THuiQuality CHuiGc::Quality() const
{
return iQuality;
}
EXPORT_C void CHuiGc::SetDisplayArea(const TRect& aDisplayArea)
{
iDisplayArea = aDisplayArea;
iClipRegion.Clear();
iClipRegion.AddRect(aDisplayArea);
}
EXPORT_C TRect CHuiGc::DisplayArea() const
{
if(iOrientation == EOrientationNormal)
{
return iDisplayArea;
}
else
{
TInt width = iDisplayArea.Width();
TInt height = iDisplayArea.Height();
/// @todo Should not always be at the top left corner.
TRect rect(TPoint(0, 0), TSize(height, width));
return rect;
}
}
EXPORT_C void CHuiGc::SetOrientation(TOrientation aOrientation)
{
iOrientation = aOrientation;
}
EXPORT_C CHuiGc::TOrientation CHuiGc::Orientation() const
{
return iOrientation;
}
EXPORT_C void CHuiGc::SetProjection(TProjection aProjection)
{
SetProjection(aProjection, iDisplayArea);
}
EXPORT_C CHuiGc::TProjection CHuiGc::Projection() const
{
return iProjection;
}
EXPORT_C void CHuiGc::SetProjection(TProjection aProjection, const TRect& aViewport)
{
iProjection = aProjection;
iProjectionViewport = aViewport;
iFrustumOffset = THuiRealPoint(0.f, 0.f);
UpdateProjection();
}
EXPORT_C void CHuiGc::SetFrustumOffset(const THuiRealPoint& aFrustumOffset)
{
if(!iDisplayArea.Width() || !iDisplayArea.Height())
{
return;
}
THuiRealPoint local = aFrustumOffset;
TRect area = DisplayArea(); // Orientation applied.
local.iX -= area.Width()/2.f;
local.iY -= area.Height()/2.f;
local.iX /= area.Width()/2.f;
local.iY /= area.Height()/2.f;
// Adjust so that the perspective correction isn't complete.
local.iX *= iFrustumOffsetCompleteness;
local.iY *= iFrustumOffsetCompleteness;
iFrustumOffset = local;
UpdateProjection();
}
EXPORT_C void CHuiGc::Disable(TFeature aFeature)
{
Enable(aFeature, EFalse);
}
EXPORT_C TRect CHuiGc::UnOrientRect(const TRect& aOrientedRect) const
{
if(iOrientation == EOrientationCCW90)
{
// Un-orient 90 degrees clockwise.
TSize size(aOrientedRect.Height(), aOrientedRect.Width());
TPoint pos(iDisplayArea.Width() - aOrientedRect.iBr.iY,
aOrientedRect.iTl.iX);
return TRect(pos, size);
}
else if(iOrientation == EOrientationCW90)
{
// Un-orient 90 degrees counterclockwise.
TSize size(aOrientedRect.Height(), aOrientedRect.Width());
TPoint pos(aOrientedRect.iTl.iY,
iDisplayArea.Height() - aOrientedRect.iBr.iX);
return TRect(pos, size);
}
else
{
// No change because there is no orientation.
return aOrientedRect;
}
}
EXPORT_C void CHuiGc::UnOrientRegion(TRegion& aOrientedRegion) const
{
if(iOrientation == EOrientationCCW90 || iOrientation == EOrientationCW90)
{
iTempRegion.Clear();
for (TInt i=0; i < aOrientedRegion.Count(); i++)
{
iTempRegion.AddRect(UnOrientRect(aOrientedRegion[i]));
}
aOrientedRegion.Copy(iTempRegion);
}
else
{
// No change because there is no orientation.
}
}
EXPORT_C void CHuiGc::PushClip()
{
RRegion* newRegion = CreateRecycledRegion();
if (newRegion)
{
newRegion->Copy(iClipRegion);
iClipRegionStack.Append(newRegion);
}
// Out of memory situation is handled by keeping separate
// counter which is checked at pop.
iClipRegionStackCount++;
}
EXPORT_C void CHuiGc::PopClip()
{
__ASSERT_ALWAYS(iClipRegionStack.Count() > 0,
THuiPanic::Panic(THuiPanic::EGcClipStackPopFromEmpty));
// Check if out of memory situation has happened in the push, so we don't pop
// because we have not really pushed anything.
if (iClipRegionStackCount > iClipRegionStack.Count())
{
iClipRegionStackCount--;
return;
}
if(iClipRegionStack.Count() > 0)
{
TInt index = iClipRegionStack.Count() - 1;
SetClipRegion(*iClipRegionStack[index]);
RRegion* region = iClipRegionStack[index];
iClipRegionStack.Remove(index);
DeleteRecycledRegion(region);
region = NULL;
iClipRegionStackCount--;
}
}
EXPORT_C void CHuiGc::SetClip(const TRect& aClipRect)
{
HUI_ASSERT(aClipRect.Width() >= 0);
HUI_ASSERT(aClipRect.Height() >= 0);
iTempRegion.Clear();
iTempRegion.AddRect(aClipRect);
CHuiGc::SetClipRegion(iTempRegion);
}
EXPORT_C TRect CHuiGc::ClipRect() const
{
// This does not really provide actually valid data anymore
// since clipping is done with a region.
return iClipRegion.BoundingRect();
}
const TRegion& CHuiGc::ClipRegion() const
{
return iClipRegion;
}
EXPORT_C void CHuiGc::Clip(const TRect& aClipRect)
{
HUI_ASSERT(aClipRect.Width() >= 0);
HUI_ASSERT(aClipRect.Height() >= 0);
if(iClipRegion.Intersects(aClipRect))
{
iTempRegion.Clear();
iTempRegion.AddRect(aClipRect);
iClipRegion.Intersect(iTempRegion);
}
else
{
// Empty clipping rectangle.
iClipRegion.Clear();
}
SetClipRegion(iClipRegion);
}
EXPORT_C void CHuiGc::Clip(const TRegion& aClipRegion)
{
iClipRegion.Intersect(aClipRegion);
SetClipRegion(iClipRegion);
}
EXPORT_C void CHuiGc::SetClipRegion(const TRegion& aClipRegion)
{
if (&aClipRegion != &iClipRegion)
{
iClipRegion.Copy(aClipRegion);
}
}
EXPORT_C void CHuiGc::CancelClipping()
{
}
EXPORT_C TInt CHuiGc::ClipStackCount() const
{
return iClipRegionStack.Count();
}
EXPORT_C void CHuiGc::SetAlign(THuiAlignHorizontal aHorizontalAlign,
THuiAlignVertical aVerticalAlign)
{
switch(aHorizontalAlign)
{
case EHuiAlignHLocale:
iAlignHorizontal = LocaleTextAlignment();
break;
case EHuiAlignHLocaleMirrored:
iAlignHorizontal = LocaleTextAlignment();
if(iAlignHorizontal == EHuiAlignHLeft)
{
iAlignHorizontal = EHuiAlignHRight;
}
else
{
iAlignHorizontal = EHuiAlignHLeft;
}
break;
default:
iAlignHorizontal = aHorizontalAlign;
break;
}
iAlignVertical = aVerticalAlign;
}
EXPORT_C THuiAlignHorizontal CHuiGc::AlignHorizontal() const
{
return iAlignHorizontal;
}
EXPORT_C THuiAlignVertical CHuiGc::AlignVertical() const
{
return iAlignVertical;
}
EXPORT_C void CHuiGc::SetPenColor(const TRgb& aColor)
{
iPenColor = aColor;
}
EXPORT_C TRgb CHuiGc::PenColor() const
{
return iPenColor;
}
EXPORT_C void CHuiGc::SetPenColorAlpha(const TRgb& aColor)
{
iPenColor = aColor;
iPenColor.SetAlpha(255);
iPenAlpha = TUint8(aColor.Alpha()); // 8 bits should be enough
}
EXPORT_C TRgb CHuiGc::PenColorAlpha() const
{
TRgb color = iPenColor;
color.SetAlpha(iPenAlpha);
return color;
}
EXPORT_C void CHuiGc::SetPenAlpha(TInt aAlpha)
{
if(aAlpha < 0)
{
aAlpha = 0;
}
else if(aAlpha > 255)
{
aAlpha = 255;
}
else
{
// for PC lint
}
iPenAlpha = TUint8(aAlpha); // 8 bits should be enough
}
EXPORT_C TInt CHuiGc::PenAlpha() const
{
return iPenAlpha;
}
EXPORT_C void CHuiGc::SetFont(THuiFont& aFont)
{
iCurrentFont = &aFont;
}
EXPORT_C void CHuiGc::DrawTextL(const TDesC& aText,
const THuiRealRect& aBounds,
TReal32 aShadow) __SOFTFP
{
if(!Font())
{
return;
}
CHuiTextMesh* mesh = CHuiStatic::Renderer().CreateTextMeshL();
CleanupStack::PushL(mesh);
mesh->SetTextL(aText, ETrue);
DrawText(*mesh, aBounds, aShadow);
CleanupStack::PopAndDestroy(mesh);
}
EXPORT_C void CHuiGc::DrawText(const CHuiTextMesh& aTextMesh,
const THuiRealRect& aBounds,
TReal32 aShadow) __SOFTFP
{
/** @todo This could be generalized: MHuiBoundedDrawable (for example) */
// Font is defined in the text mesh.
// Real bounding size of the text.
TSize textBounds = aTextMesh.Extents();
// Reference origin for the text.
TPoint offset = AlignOffset(aBounds.Size());
// Actual starting point for glyphs.
THuiRealPoint pos = aBounds.iTl - offset;
switch(AlignHorizontal())
{
case EHuiAlignHRight:
pos.iX -= textBounds.iWidth;
break;
case EHuiAlignHCenter:
pos.iX -= textBounds.iWidth / 2; // or 2.f?
break;
default:
break;
}
switch(AlignVertical())
{
case EHuiAlignVBottom:
pos.iY -= textBounds.iHeight;
break;
case EHuiAlignVCenter:
pos.iY -= textBounds.iHeight / 2; // or 2.f?
break;
default:
break;
}
// Apply the position.
Push(EHuiGcMatrixModel);
Translate(EHuiGcMatrixModel, pos.iX, pos.iY, 0.f);
aTextMesh.Draw(*this, aShadow);
Pop(EHuiGcMatrixModel);
}
EXPORT_C void CHuiGc::DrawImage(const THuiImage& aImage,
const THuiRealPoint& aPos,
const THuiRealSize& aSize)
{
if(!aImage.HasTexture())
{
return;
}
THuiRealPoint topLeft = aPos + AlignOffset(aSize);
UpdateColor();
// Update texture preferred size, toolkit may use this information to resize texture
NotifyImagePreferredSize(aImage, THuiRealRect(topLeft,aSize));
DoDrawImage(aImage, topLeft, aSize);
}
EXPORT_C void CHuiGc::DrawImages(const THuiImage& aImage,
const THuiImage& aImage2,
const THuiRealPoint& aPos,
const THuiRealSize& aSize)
{
if(!aImage.HasTexture() || !aImage2.HasTexture())
{
return;
}
THuiRealPoint topLeft = aPos + AlignOffset(aSize);
UpdateColor();
// Update texture preferred size, toolkit may use this information to resize texture
NotifyImagePreferredSize(aImage, THuiRealRect(topLeft,aSize));
NotifyImagePreferredSize(aImage2, THuiRealRect(topLeft,aSize));
DoDrawImages(aImage, aImage2, topLeft, aSize);
}
EXPORT_C void CHuiGc::DrawImage(const THuiImage& aImage,
const THuiRealRect& aBounds)
{
if(!aImage.HasTexture())
{
return;
}
TSize space(TInt(aBounds.Width() - aImage.Texture().Size().iWidth),
TInt(aBounds.Height() - aImage.Texture().Size().iHeight));
TPoint topLeft = aBounds.iTl - AlignOffset(space);
UpdateColor();
// Update texture preferred size, toolkit may use this information to resize texture
NotifyImagePreferredSize(aImage, aBounds);
DoDrawImage(aImage, topLeft, aImage.Texture().Size());
}
EXPORT_C void CHuiGc::DrawImage( const THuiImage& aImage,
const THuiRealSize& aSize,
const THuiRealRect& aBounds)
{
if(!aImage.HasTexture())
{
return;
}
TSize space(TInt(aBounds.Width() - aSize.iWidth),
TInt(aBounds.Height() - aSize.iHeight));
TPoint topLeft = aBounds.iTl - AlignOffset(space);
UpdateColor();
// Update texture preferred size, toolkit may use this information to resize texture
NotifyImagePreferredSize(aImage, THuiRealRect(topLeft,aSize));
DoDrawImage(aImage, topLeft, aSize);
}
EXPORT_C void CHuiGc::DrawImages(const THuiImage& aImage,
const THuiImage& aImage2,
const THuiRealSize& aSize,
const THuiRealRect& aBounds)
{
if(!aImage.HasTexture() || !aImage2.HasTexture())
{
return;
}
TSize space(TInt(aBounds.Width() - aSize.iWidth),
TInt(aBounds.Height() - aSize.iHeight));
TPoint topLeft = aBounds.iTl - AlignOffset(space);
UpdateColor();
// Update texture preferred size, toolkit may use this information to resize texture
NotifyImagePreferredSize(aImage, THuiRealRect(topLeft,aSize));
NotifyImagePreferredSize(aImage2, THuiRealRect(topLeft,aSize));
DoDrawImages(aImage, aImage2, topLeft, aSize);
}
EXPORT_C THuiFont* CHuiGc::Font()
{
return iCurrentFont;
}
EXPORT_C TPoint CHuiGc::AlignOffset(const TSize& aBounds) const
{
TPoint offset(0, 0);
switch(iAlignHorizontal)
{
case EHuiAlignHRight:
offset.iX = -aBounds.iWidth;
break;
case EHuiAlignHCenter:
offset.iX = -aBounds.iWidth / 2;
break;
default:
offset.iX = 0;
break;
}
switch(iAlignVertical)
{
case EHuiAlignVBottom:
offset.iY = -aBounds.iHeight;
break;
case EHuiAlignVCenter:
offset.iY = -aBounds.iHeight / 2;
break;
default:
offset.iY = 0;
break;
}
return offset;
}
EXPORT_C TReal32 CHuiGc::Offset2D() const __SOFTFP
{
/// @todo Make this configurable.
//return 100.0 / FovFactor();
return KDefaultOffset2D;
}
EXPORT_C void
CHuiGc::SetWindowTransformation(CHuiTransformation* aWindowTransformation)
{
iWindowTransform = aWindowTransformation;
}
EXPORT_C TReal32 CHuiGc::NearClipDistance() const __SOFTFP
{
return iNearClipDistance;
}
EXPORT_C TReal32 CHuiGc::FarClipDistance() const __SOFTFP
{
return iFarClipDistance;
}
EXPORT_C TRect CHuiGc::ProjectionViewport() const
{
return iProjectionViewport;
}
EXPORT_C TRect CHuiGc::RawDisplayArea() const
{
return iDisplayArea;
}
EXPORT_C const CHuiTransformation* CHuiGc::WindowTransformation() const
{
return iWindowTransform;
}
EXPORT_C THuiRealPoint CHuiGc::FrustumOffset() const
{
return iFrustumOffset;
}
EXPORT_C TReal32 CHuiGc::FovFactor() const __SOFTFP
{
return 1.f / iFovFactor;
}
EXPORT_C THuiAlignHorizontal CHuiGc::LocaleTextAlignment()
{
if(CHuiStatic::LayoutMirrored())
{
return EHuiAlignHRight;
}
return EHuiAlignHLeft;
}
EXPORT_C void CHuiGc::DrawStretchImage(TStretchMode /*aMode*/,
const THuiImage& aImage,
const THuiRealRect& aRect,
TInt /*aStretchStartWidth*/,
TInt /*aStretchEndWidth*/)
{
// Update texture preferred size, toolkit may use this information to resize texture
NotifyImagePreferredSize(aImage, aRect);
}
EXPORT_C void CHuiGc::NotifyImagePreferredSize(const THuiImage& aImage, THuiRealRect aRect)
{
const CHuiTexture* texture = aImage.ImageDefaultTexture();
if (texture)
{
CHuiTexture* notifiedTexture = const_cast<CHuiTexture*> (texture);
if (notifiedTexture->IsAutoSizeCalculationEnabled())
{
TReal32 xratio = 1;
TReal32 yratio = 1;
// Take into account used texture coordinates of THuiImage
TReal32 relativeWidth = aImage.BottomRight().iX - aImage.TopLeft().iX;
TReal32 relativeHeight = aImage.BottomRight().iY - aImage.TopLeft().iY;
if (relativeWidth != 0.f && relativeHeight != 0.f)
{
xratio *= (1/relativeWidth);
yratio *= (1/relativeHeight);
}
TReal32 width = aRect.Size().iWidth * xratio;
TReal32 height = aRect.Size().iHeight * yratio;
// Take into account window transformation scale (To enable HiRes TV-OUT textures)
// Other transformations are ignored for now. Possible those could be optionally
// taken into account as well if resizing algortihm is lazy enough.
CHuiTransformation* wt = (CHuiTransformation*)WindowTransformation();
if (wt)
{
TInt count = wt->Count();
for (TInt i=0;i<count;i++)
{
CHuiTransformation::TTransform& step = wt->Step(i);
if (step.iType == CHuiTransformation::ETypeScale)
{
width = width * step.iParams[EHuiTransformParamTranslateX].Now();
height = height * step.iParams[EHuiTransformParamTranslateY].Now();
}
}
}
notifiedTexture->NotifyPreferredSize(THuiRealSize(width,height));
}
}
}
EXPORT_C void CHuiGc::TransformDirtyRect(THuiRealRect& /*aRect*/)
{
// No sensible implementation for HuiGc base class. Implement at least in
// HuiBitgdiGc child class.
}
EXPORT_C void CHuiGc::EnableTransformedClippingRects(TBool /*aEnable*/)
{
// No sensible implementation for HuiGc base class. Implement at least in
// HuiBitgdiGc child class.
}
EXPORT_C void CHuiGc::SetClip(const TRect& /*aClipRect*/, TBool /*aTransformClipRect*/)
{
// No sensible implementation for HuiGc base class. Implement at least in
// HuiBitgdiGc child class.
}
EXPORT_C void CHuiGc::GcExtension(const TUid& /*aExtensionUid*/, TAny** aExtensionParams)
{
// If no extension with given UID was found, indicate it by returning null
*aExtensionParams = NULL;
}
RRegion* CHuiGc::CreateRecycledRegion()
{
RRegion* region = NULL;
if (iRecycledRegions.Count())
{
// If there are recycled regionbufs, use one of those
TInt last = iRecycledRegions.Count() - 1;
region = iRecycledRegions[last];
iRecycledRegions.Remove(last);
}
else
{
// No recycled regionbufs available, create new one
region = new RRegionBuf<KHuiGcClipRegionGranularity>();
}
return region;
}
void CHuiGc::DeleteRecycledRegion(RRegion* aRegion)
{
TInt error = KErrNone;
if (iRecycledRegions.Count() < KHuiMaxRecycledRegionCount)
{
aRegion->Clear();
error = iRecycledRegions.Append(aRegion);
if (error)
{
aRegion->Destroy();
}
}
else
{
aRegion->Destroy();
}
}