graphicsdeviceinterface/gdi/sgdi/GlyphSel.cpp
changeset 0 5d03bc08d59c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graphicsdeviceinterface/gdi/sgdi/GlyphSel.cpp	Tue Feb 02 01:47:50 2010 +0200
@@ -0,0 +1,593 @@
+// Copyright (c) 2003-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:
+//
+
+/**
+ @file
+ @internalComponent
+*/
+
+
+#include <gdi.h>
+#include <openfont.h>
+#include "GlyphSel.h"
+#include "GDIPANIC.h"
+
+
+static const TText16 KLatinGlyph_SoftHyphen = 0x00AD;
+
+
+// 
+//
+// TUtf32Iterator Class definition
+//
+//
+
+
+TUint TUtf32Iterator::UTF16ToTChar(const TText16* a)
+/**
+ This routine takes an encoded UTF16 byte array and decodes the
+ first character at the start of the array and returns it as a TChar.
+ If the char is "not a char" character 0xFFFF results.
+@param a 
+ UTF16 byte array to be decoded.
+@param aPr
+ Position pointer 'a' derived from, incremented if surragote pairs decoded.
+@return
+ The character value in UTF32 format or 0xFFFF it not a character.
+*/
+	{
+	// Is next char a surrogate?
+	if (0xD800 == (a[0] & 0xF800)) 
+		{
+		// Is it a high surrogate in the range D800..DBFF?
+		if (0xD800 == (a[0] & 0xFC00)) 
+			{
+			// Its a high surrogate, is the next char a low surrogate?
+			if (0xDC00 == (a[1] & 0xFC00))
+				{
+				// It's a low surrogate
+				return ((a[0] - 0xd7f7) << 10) + a[1];
+				}
+			else
+				return 0xFFFF;
+			}
+		else
+			return 0xFFFF;
+		}
+	else
+		return a[0];
+	}
+
+
+TUtf32Iterator::TUtf32Iterator(const TText16* aStart, const TText16* aEnd, TInt aStartingIndex)
+/**
+ Construct iterator given UTF16 encoded byte array.
+@param aStart
+ Start address of the array.
+@param aEnd
+ Address of the byte just beyond the end of the array.
+@param aStartingIndex
+ Optional UTF16 offset into the array to initialise the current position to.
+@panic EGdiPanic_InvalidInputParam
+ Raised when array start if passed the array end.
+*/
+: iStart(aStart), iCurrent(aStart+aStartingIndex), iEnd(aEnd), iChar(0xffff)
+	{
+	GDI_ASSERT_DEBUG(iStart < iEnd, EGdiPanic_InvalidInputParam);
+	
+	if (iCurrent > iEnd) 
+	    iCurrent = iEnd;
+	else if (iCurrent < iStart)
+	    iCurrent = iStart;
+	else
+		{
+		// Sanatise array end checking for an unpaired surrogate value
+		// so that UTF16ToTChar() does not read off the end of the array.
+        if (0xD800 == (iEnd[-1] & 0xFC00))
+			{
+			if (iCurrent == iEnd-1)
+				++iCurrent;
+			else
+				--iEnd;
+			}
+
+		// Setup initial position UTF32 character value 
+		iChar = UTF16ToTChar(iCurrent);
+		}
+	}
+
+
+TChar TUtf32Iterator::Next()
+/**
+Moves the iterator forward to the next valid UTF32 character value.
+@return TChar The next character in the text towards the end.
+@panic EGdiPanic_OutOfText
+Raised when there is no next position to move to.
+*/
+	{
+	GDI_ASSERT_DEBUG(iCurrent < iEnd, EGdiPanic_OutOfText);
+
+    iCurrent += (iChar > 0xffff) ? 2 : 1;
+    if (iCurrent < iEnd)
+	    iChar = UTF16ToTChar(iCurrent);
+	else
+	    iChar = 0xFFFF;  
+	return iChar;
+	}
+
+
+TChar TUtf32Iterator::Prev()
+/**
+Moves the iterator backwards to the next valid UTF32 character value.
+@return TChar The prev character in the text towards the start.
+@panic EGdiPanic_OutOfText Raised when there is no next position to move to.
+*/
+	{
+	GDI_ASSERT_DEBUG(iCurrent >= iStart, EGdiPanic_OutOfText);
+
+    --iCurrent;
+    if (iCurrent >= iStart)
+	    iChar = UTF16ToTChar(iCurrent);
+	else
+	    iChar = 0xFFFF;
+	return iChar; 
+	}
+
+
+void TUtf32Iterator::SetPos(TInt aPos)
+/**
+ Moves the iterator to the position specified by array start+offset.
+@param aPos
+  UTF16 offset into the array to set the current position to.
+@panic EGdiPanic_OutOfText
+ Raised when there is no next position to move to.
+*/
+	{
+	GDI_ASSERT_DEBUG(iStart+aPos <= iEnd, EGdiPanic_OutOfText);
+	GDI_ASSERT_DEBUG(iStart+aPos >= iStart, EGdiPanic_OutOfText);
+
+	iCurrent = iStart+aPos;
+	iChar = UTF16ToTChar(iCurrent);
+	}
+
+
+TUint TUtf32Iterator::Get(TInt offset)
+/**
+ Returns the UTF32 char value at the offset specified. 0xFFFF may be returned
+ for unpaired surrogate and noncharacters. Does not change the current 
+ position.
+@param offset
+ UTF16 offset from current iterator position to get UTF32 char form. 
+@return TChar
+ UTF32 char value found at the iterator+offset, or 0xFFFF in error.
+@panic EGdiPanic_OutOfText
+ Raised when offset found to be outside the bounds of the original text array.
+*/
+	{
+	GDI_ASSERT_DEBUG(iCurrent+offset >= iStart, EGdiPanic_OutOfText);
+	GDI_ASSERT_DEBUG(iCurrent+offset < iEnd, EGdiPanic_OutOfText);
+	
+	return UTF16ToTChar(iCurrent+offset);
+	}
+
+
+TChar TUtf32Iterator::GetThenNext()
+/**
+ Return the UTF32 value at the current position.
+@return TChar
+ UTF32 value currently pointed to by iterator.
+@panic EGdiPanic_EndOfText
+ Raised when current iterator position is not valid.
+*/
+	{
+	GDI_ASSERT_DEBUG(iCurrent < iEnd, EGdiPanic_OutOfText);
+
+	TChar current(iChar);
+    iCurrent += (iChar > 0xffff) ? 2 : 1;
+    if (iCurrent < iEnd)
+	    iChar = UTF16ToTChar(iCurrent);
+	else
+	    iChar = 0xFFFF;  
+	return current;
+	}
+
+
+TChar TUtf32Iterator::GetThenPrev()
+/**
+ Return the UTF32 value at the current position.
+@return TChar
+ UTF32 value currently pointed to by iterator.
+@panic EGdiPanic_EndOfText
+ Raised when current iterator position is not valid.
+*/
+	{
+	GDI_ASSERT_DEBUG(iCurrent >= iStart, EGdiPanic_OutOfText);
+
+	TChar current(iChar);
+    --iCurrent;
+    if (iCurrent >= iStart)
+	    iChar = UTF16ToTChar(iCurrent);
+	else
+	    iChar = 0xFFFF;
+	return current;
+	}
+	
+	
+TInt TUtf32Iterator::LengthToStart() const
+/**
+ Returns the number of TText16 codes between the start point and its
+ current position.
+@return TInt
+ Number of TText16 characters between array start and current iterator
+ position.
+*/
+	{
+	return iCurrent-iStart;
+	}
+
+
+TInt TUtf32Iterator::LengthToEnd() const
+/**
+ Returns the number of remaining TText16 codes still ahead of the
+ iterator.
+@return TInt
+ Number of TText16 characters between array current iterator position
+ and the end of the array.
+*/
+	{
+	return iEnd - iCurrent;
+	}
+
+const TText16* TUtf32Iterator::CurrentPosition() const
+	{
+	return iCurrent;
+	}
+
+void TUtf32Iterator::SetCurrentPosition(const TText16* a)
+	{
+	iCurrent = a;
+	}
+
+// 
+//
+// TGlyphSelectionState Class definition
+//
+//
+
+
+/** 
+ The Unicode Combining Class values recognised by the
+ GlyphSelUtils::CombineLastGlyphToBase method.
+@internalComponent
+*/
+enum TCombiningClass
+	{
+	EArabicFathatan = 27,
+	EArabicDammatan = 28,
+	EArabicKasratan = 29,
+	EArabicFatha = 30,
+	EArabicDamma = 31,
+	EArabicKasra = 32,
+	EArabicShadda = 33,
+	EArabicSukun = 34,
+	ECombineBelowLeftAttached = 200,
+	ECombineBelowAttached = 202,
+	ECombineBelowRightAttached = 204,
+	ECombineLeftAttached = 208,
+	ECombineRightAttached = 210,
+	ECombineAboveLeftAttached = 212,
+	ECombineAboveAttached = 214,
+	ECombineAboveRightAttached = 216,
+	ECombineBelowLeft = 218,
+	ECombineBelow = 220,
+	ECombineBelowRight = 222,
+	ECombineLeft = 224,
+	ECombineRight = 226,
+	ECombineAboveLeft = 228,
+	ECombineAbove = 230,
+	ECombineAboveRight = 232
+	};
+
+
+/**
+ This method is called to attach (by adjusing its bounding box) the current end
+ glyph in the output array of iParam to the base glyph bounding box based on
+ the Unicode combining class of the character.
+@param aGss
+ The general input/output glyph selection data for the routine.
+@param aGss.iOutput
+ Input: Glyph cluster with last glyph an actual combining character. Output:
+ Bounding box of last glyph adjusted according to char combining class.
+@param aFirstDiacritic
+ Which character in the output array to treat as the first diacritic of the
+ cluster. Usually 1, but can be more if the base class is a ligature.
+*/
+void TGlyphSelectionState::CombineLastGlyphToBase(const TRect& aBase, TInt aFirstDiacritic)
+	{
+	// Get the bounds of all the base characters.
+	TRect base = aBase;
+	int last = iParam.iOutputGlyphs-1;
+	for (int i = aFirstDiacritic; i < last; i++)
+		base.BoundingRect(iParam.iOutput[i].iBounds);
+
+	// Calculate the attachment points.
+	TRect& r = iParam.iOutput[last].iBounds;
+	int w = r.Width();
+	int h = r.Height();
+	int t = r.iTl.iY;
+	int l = r.iTl.iX;
+	int left = base.iTl.iX;
+	int center = base.iTl.iX + (base.Width() - w) / 2;
+	int right = base.iBr.iX - w;
+	int below = base.iBr.iY;
+	int above = base.iTl.iY - h;
+	int left_of = left - w;
+	int right_of = right + w;
+	int xGap = 1;
+	int yGap = iFont->HeightInPixels()/10;
+	
+	// Select attachment based on combining class.
+	switch (iCombCls)
+		{
+		case ECombineBelowLeftAttached:
+			t = below;
+			l = left;
+			break;
+		case ECombineBelowAttached:
+			t = below;
+			l = center;
+			break;
+		case ECombineBelowRightAttached:
+			t = below;
+			l = right;
+			break;
+		case ECombineLeftAttached:
+			l = left_of;
+			break;
+		case ECombineRightAttached:
+			l = right_of;
+			break;
+		case ECombineAboveLeftAttached:
+			t = above;
+			l = left;
+			break;
+		case ECombineAboveAttached:
+			t = above;
+			l = center;
+			break;
+		case ECombineAboveRightAttached:
+			t = above;
+			l = right;
+			break;
+		case ECombineBelowLeft:
+			t = below + yGap;
+			l = left;
+			break;
+		case ECombineBelow:
+		case EArabicKasratan:
+		case EArabicKasra:
+			t = below + yGap;
+			l = center;
+			break;
+		case ECombineBelowRight:
+			t = below + yGap;
+			l = right;
+			break;
+		case ECombineLeft:
+			l = left_of - xGap;
+			break;
+		case ECombineRight:
+			l = right_of + xGap;
+			break;
+		case ECombineAboveLeft:
+			t = above - yGap;
+			l = left;
+			break;
+		case ECombineAbove:
+		case EArabicFathatan:
+		case EArabicDammatan:
+		case EArabicFatha:
+		case EArabicDamma:
+		case EArabicShadda:
+		case EArabicSukun:
+			t = above - yGap;
+			l = center;
+			break;
+		case ECombineAboveRight:
+			t = above - yGap;
+			l = right;
+			break;
+		default:
+			l = center;
+			break;
+		}
+
+	// Adjust the bounding box of the last glyph to fix position
+	// based on the characters combining class. For speed, do directly.
+	// r.SetRect(l,t,l + w,t + h);
+	r.iTl.iX = l;
+	r.iTl.iY = t;
+	r.iBr.iX = l+w;
+	r.iBr.iY = t+h;
+	}
+
+
+TBool TGlyphSelectionState::AppendGlyphToCluster(TUint aCode)
+/**
+ This common method is used by glyph selector classes to add a glyph to
+ the end of the aGss.iParam output field filling in all the glyph info 
+ needed.
+@param aCode
+ The Unicode character for which a glyph should be appended.
+@param aGss
+ The general input/output glyph selection data for the routine. 
+@return TBool
+ ETrue when successful, EFalse when failure occurs e..g no char data, overflow
+*/
+	{
+	// Setup reference to next free glyph record we need to update.
+	GDI_ASSERT_DEBUG(iParam.iOutputGlyphs < CFont::TPositionParam::EMaxOutputGlyphs, 
+		EGdiPanic_InvalidInputParam);
+	   
+	CFont::TPositionParam::TOutput* output = iParam.iOutput+iParam.iOutputGlyphs;
+
+	// Retrieve the glyph details from the Font. Essential to proceed, abort
+	// if not available.
+	TOpenFontCharMetrics metrics;
+	if (iFont->GetCharacterData(aCode, metrics, output->iBitmap, 
+		output->iBitmapSize) == CFont::ENoCharacterData)
+		return EFalse;
+
+	// Set code point of glyph in output record.
+	output->iCode = aCode;
+	
+	// Set the glyph's bounds in the output record and record pen advancement.
+	if (iParam.iDirection == CFont::EVertical)
+		{
+		metrics.GetVertBounds(output->iBounds);
+		iAdvance.iHeight = Max(iAdvance.iHeight, metrics.VertAdvance());
+		}
+	else
+		{
+		metrics.GetHorizBounds(output->iBounds);
+		iAdvance.iWidth = Max(iAdvance.iWidth, metrics.HorizAdvance());
+		}
+
+	// Next adjust the glyph's bounding box to offset it from the pen
+	// position (origin of drawing). For speed increment attributes directly.
+	// output->iBounds.Move(aGss.iParam.iPen);
+	output->iBounds.iTl.iX += iParam.iPen.iX;
+	output->iBounds.iBr.iX += iParam.iPen.iX;
+	output->iBounds.iTl.iY += iParam.iPen.iY;
+	output->iBounds.iBr.iY += iParam.iPen.iY;
+	
+	// Before we exit with success, increment the glyph array counter.
+	// for the new glyph we've added here.
+	iParam.iOutputGlyphs++;
+	return ETrue;
+	}
+
+
+// 
+//
+// GlyphSelector_SoftHyphen Class definition
+//
+//
+
+TBool GlyphSelector_SoftHyphen::Process(TGlyphSelectionState& aGss, RShapeInfo&) 
+/**
+@see GlyphSelUtils 
+ See this class for the method description.
+*/
+	{
+ 	aGss.iText.Next();
+	if (!aGss.iText.AtEnd())
+		{
+		// Here we skip & don't output hyphen since its not at the end a line.
+		aGss.iPen = TGlyphSelectionState::EPenAdvance_No;
+		}
+	else
+		{
+		// If we reach here we must output hyphen.
+		if (!aGss.AppendGlyphToCluster(KLatinGlyph_SoftHyphen))
+			return EFalse;
+		
+		aGss.iPen = TGlyphSelectionState::EPenAdvance_Yes;
+		}
+
+	// Logic to determine if we are now at the end of the glyph cluster.
+	// Default logic, based on whether a combining mark follows or not.
+	aGss.iClusterState = 
+		(!aGss.iText.AtEnd() &&
+			((aGss.iText.Get().GetCategory() & 0xF0) == TChar::EMarkGroup)) ?
+			TGlyphSelectionState::EGClusterNotComplete : TGlyphSelectionState::EGClusterComplete;
+
+	return ETrue;
+	}
+
+
+// 
+//
+// GlyphSelector_Default Class definition
+//
+//
+
+
+TBool GlyphSelector_Default::Process(TGlyphSelectionState& aGss, RShapeInfo&) 
+/**
+@see GlyphSelUtils 
+ See this class for the method description.
+*/
+	{
+	
+	// In this method we always output the glyph.
+	if (!aGss.AppendGlyphToCluster(aGss.iText.GetThenNext()))
+		return EFalse;
+
+	// Adjust glyph's bounds further to position this character if it is a
+	// combining mark
+	if (aGss.IsCombiningClass())
+		{
+		aGss.iPen = TGlyphSelectionState::EPenAdvance_No;
+		
+		TRect baseBounds(aGss.iParam.iOutput[0].iBounds);
+		// Get first character in this glyph cluster. In this default process function, the iCode should 
+		// be Unicode Point Code.  
+		TChar startChar = TChar(aGss.iParam.iOutput[0].iCode);
+		// Character index in the output array to treat as the first diacritic of the
+		// cluster. It will be used as first character for combine to. usually 1, but when
+		// the cluster starts with a combining mark, it should be set to 0.  
+		TInt indexOfFirstCombining = 1; 
+		TInt startCharCat = startChar.GetCategory() & 0xF0;
+		
+		// if the first character in this cluster is a combining mark or a graphically empty character, 
+		// (such as a space Character0x0020), a fake bound, formed from the Ascent of the font, will be 
+		// used for combining
+		if ((startCharCat == TChar::EMarkGroup) || baseBounds.Size() == TSize(0,0)) 
+			{
+			// Determine the height of the combining glyph.
+			TInt glyphHeight = 0;
+			if (aGss.iParam.iOutputGlyphs == 1)
+				{
+				glyphHeight = aGss.iParam.iOutput[0].iBitmapSize.iHeight;
+				}
+			else
+				{
+				glyphHeight = aGss.iParam.iOutput[1].iBitmapSize.iHeight;
+				}
+			// Adjust Y values to a ficticious but reasonable range for it to combine to using the glyph height to adjust correctly below the font ascent.
+			baseBounds.iTl.iY = aGss.iParam.iPen.iY - aGss.iFont->AscentInPixels() + glyphHeight; //modest ascender
+			baseBounds.iBr.iY = aGss.iParam.iPen.iY; //No descender
+			}
+		
+		if (startCharCat == TChar::EMarkGroup)
+			indexOfFirstCombining = 0;
+								
+		aGss.CombineLastGlyphToBase(baseBounds, indexOfFirstCombining);
+		aGss.iGlyphPostCombine = TGlyphSelectionState::EGPostCombine_Yes;
+		}	
+	else
+		aGss.iPen = TGlyphSelectionState::EPenAdvance_Yes;
+
+	// Logic to determine if we are now at the end of the glyph cluster.
+	// Default logic, based on whether a combining mark follows or not.
+	aGss.iClusterState = 
+		(!aGss.iText.AtEnd() &&
+		 ((aGss.iText.Get().GetCategory() & 0xF0) == TChar::EMarkGroup)) ?
+			TGlyphSelectionState::EGClusterNotComplete : TGlyphSelectionState::EGClusterComplete;
+
+	return ETrue;
+	}
+