os/graphics/graphicsdeviceinterface/gdi/sgdi/GlyphSel.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 // Copyright (c) 2003-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 //
    15 
    16 /**
    17  @file
    18  @internalComponent
    19 */
    20 
    21 
    22 #include <gdi.h>
    23 #include <openfont.h>
    24 #include "GlyphSel.h"
    25 #include "GDIPANIC.h"
    26 
    27 
    28 static const TText16 KLatinGlyph_SoftHyphen = 0x00AD;
    29 
    30 
    31 // 
    32 //
    33 // TUtf32Iterator Class definition
    34 //
    35 //
    36 
    37 
    38 TUint TUtf32Iterator::UTF16ToTChar(const TText16* a)
    39 /**
    40  This routine takes an encoded UTF16 byte array and decodes the
    41  first character at the start of the array and returns it as a TChar.
    42  If the char is "not a char" character 0xFFFF results.
    43 @param a 
    44  UTF16 byte array to be decoded.
    45 @param aPr
    46  Position pointer 'a' derived from, incremented if surragote pairs decoded.
    47 @return
    48  The character value in UTF32 format or 0xFFFF it not a character.
    49 */
    50 	{
    51 	// Is next char a surrogate?
    52 	if (0xD800 == (a[0] & 0xF800)) 
    53 		{
    54 		// Is it a high surrogate in the range D800..DBFF?
    55 		if (0xD800 == (a[0] & 0xFC00)) 
    56 			{
    57 			// Its a high surrogate, is the next char a low surrogate?
    58 			if (0xDC00 == (a[1] & 0xFC00))
    59 				{
    60 				// It's a low surrogate
    61 				return ((a[0] - 0xd7f7) << 10) + a[1];
    62 				}
    63 			else
    64 				return 0xFFFF;
    65 			}
    66 		else
    67 			return 0xFFFF;
    68 		}
    69 	else
    70 		return a[0];
    71 	}
    72 
    73 
    74 TUtf32Iterator::TUtf32Iterator(const TText16* aStart, const TText16* aEnd, TInt aStartingIndex)
    75 /**
    76  Construct iterator given UTF16 encoded byte array.
    77 @param aStart
    78  Start address of the array.
    79 @param aEnd
    80  Address of the byte just beyond the end of the array.
    81 @param aStartingIndex
    82  Optional UTF16 offset into the array to initialise the current position to.
    83 @panic EGdiPanic_InvalidInputParam
    84  Raised when array start if passed the array end.
    85 */
    86 : iStart(aStart), iCurrent(aStart+aStartingIndex), iEnd(aEnd), iChar(0xffff)
    87 	{
    88 	GDI_ASSERT_DEBUG(iStart < iEnd, EGdiPanic_InvalidInputParam);
    89 	
    90 	if (iCurrent > iEnd) 
    91 	    iCurrent = iEnd;
    92 	else if (iCurrent < iStart)
    93 	    iCurrent = iStart;
    94 	else
    95 		{
    96 		// Sanatise array end checking for an unpaired surrogate value
    97 		// so that UTF16ToTChar() does not read off the end of the array.
    98         if (0xD800 == (iEnd[-1] & 0xFC00))
    99 			{
   100 			if (iCurrent == iEnd-1)
   101 				++iCurrent;
   102 			else
   103 				--iEnd;
   104 			}
   105 
   106 		// Setup initial position UTF32 character value 
   107 		iChar = UTF16ToTChar(iCurrent);
   108 		}
   109 	}
   110 
   111 
   112 TChar TUtf32Iterator::Next()
   113 /**
   114 Moves the iterator forward to the next valid UTF32 character value.
   115 @return TChar The next character in the text towards the end.
   116 @panic EGdiPanic_OutOfText
   117 Raised when there is no next position to move to.
   118 */
   119 	{
   120 	GDI_ASSERT_DEBUG(iCurrent < iEnd, EGdiPanic_OutOfText);
   121 
   122     iCurrent += (iChar > 0xffff) ? 2 : 1;
   123     if (iCurrent < iEnd)
   124 	    iChar = UTF16ToTChar(iCurrent);
   125 	else
   126 	    iChar = 0xFFFF;  
   127 	return iChar;
   128 	}
   129 
   130 
   131 TChar TUtf32Iterator::Prev()
   132 /**
   133 Moves the iterator backwards to the next valid UTF32 character value.
   134 @return TChar The prev character in the text towards the start.
   135 @panic EGdiPanic_OutOfText Raised when there is no next position to move to.
   136 */
   137 	{
   138 	GDI_ASSERT_DEBUG(iCurrent >= iStart, EGdiPanic_OutOfText);
   139 
   140     --iCurrent;
   141     if (iCurrent >= iStart)
   142 	    iChar = UTF16ToTChar(iCurrent);
   143 	else
   144 	    iChar = 0xFFFF;
   145 	return iChar; 
   146 	}
   147 
   148 
   149 void TUtf32Iterator::SetPos(TInt aPos)
   150 /**
   151  Moves the iterator to the position specified by array start+offset.
   152 @param aPos
   153   UTF16 offset into the array to set the current position to.
   154 @panic EGdiPanic_OutOfText
   155  Raised when there is no next position to move to.
   156 */
   157 	{
   158 	GDI_ASSERT_DEBUG(iStart+aPos <= iEnd, EGdiPanic_OutOfText);
   159 	GDI_ASSERT_DEBUG(iStart+aPos >= iStart, EGdiPanic_OutOfText);
   160 
   161 	iCurrent = iStart+aPos;
   162 	iChar = UTF16ToTChar(iCurrent);
   163 	}
   164 
   165 
   166 TUint TUtf32Iterator::Get(TInt offset)
   167 /**
   168  Returns the UTF32 char value at the offset specified. 0xFFFF may be returned
   169  for unpaired surrogate and noncharacters. Does not change the current 
   170  position.
   171 @param offset
   172  UTF16 offset from current iterator position to get UTF32 char form. 
   173 @return TChar
   174  UTF32 char value found at the iterator+offset, or 0xFFFF in error.
   175 @panic EGdiPanic_OutOfText
   176  Raised when offset found to be outside the bounds of the original text array.
   177 */
   178 	{
   179 	GDI_ASSERT_DEBUG(iCurrent+offset >= iStart, EGdiPanic_OutOfText);
   180 	GDI_ASSERT_DEBUG(iCurrent+offset < iEnd, EGdiPanic_OutOfText);
   181 	
   182 	return UTF16ToTChar(iCurrent+offset);
   183 	}
   184 
   185 
   186 TChar TUtf32Iterator::GetThenNext()
   187 /**
   188  Return the UTF32 value at the current position.
   189 @return TChar
   190  UTF32 value currently pointed to by iterator.
   191 @panic EGdiPanic_EndOfText
   192  Raised when current iterator position is not valid.
   193 */
   194 	{
   195 	GDI_ASSERT_DEBUG(iCurrent < iEnd, EGdiPanic_OutOfText);
   196 
   197 	TChar current(iChar);
   198     iCurrent += (iChar > 0xffff) ? 2 : 1;
   199     if (iCurrent < iEnd)
   200 	    iChar = UTF16ToTChar(iCurrent);
   201 	else
   202 	    iChar = 0xFFFF;  
   203 	return current;
   204 	}
   205 
   206 
   207 TChar TUtf32Iterator::GetThenPrev()
   208 /**
   209  Return the UTF32 value at the current position.
   210 @return TChar
   211  UTF32 value currently pointed to by iterator.
   212 @panic EGdiPanic_EndOfText
   213  Raised when current iterator position is not valid.
   214 */
   215 	{
   216 	GDI_ASSERT_DEBUG(iCurrent >= iStart, EGdiPanic_OutOfText);
   217 
   218 	TChar current(iChar);
   219     --iCurrent;
   220     if (iCurrent >= iStart)
   221 	    iChar = UTF16ToTChar(iCurrent);
   222 	else
   223 	    iChar = 0xFFFF;
   224 	return current;
   225 	}
   226 	
   227 	
   228 TInt TUtf32Iterator::LengthToStart() const
   229 /**
   230  Returns the number of TText16 codes between the start point and its
   231  current position.
   232 @return TInt
   233  Number of TText16 characters between array start and current iterator
   234  position.
   235 */
   236 	{
   237 	return iCurrent-iStart;
   238 	}
   239 
   240 
   241 TInt TUtf32Iterator::LengthToEnd() const
   242 /**
   243  Returns the number of remaining TText16 codes still ahead of the
   244  iterator.
   245 @return TInt
   246  Number of TText16 characters between array current iterator position
   247  and the end of the array.
   248 */
   249 	{
   250 	return iEnd - iCurrent;
   251 	}
   252 
   253 const TText16* TUtf32Iterator::CurrentPosition() const
   254 	{
   255 	return iCurrent;
   256 	}
   257 
   258 void TUtf32Iterator::SetCurrentPosition(const TText16* a)
   259 	{
   260 	iCurrent = a;
   261 	}
   262 
   263 // 
   264 //
   265 // TGlyphSelectionState Class definition
   266 //
   267 //
   268 
   269 
   270 /** 
   271  The Unicode Combining Class values recognised by the
   272  GlyphSelUtils::CombineLastGlyphToBase method.
   273 @internalComponent
   274 */
   275 enum TCombiningClass
   276 	{
   277 	EArabicFathatan = 27,
   278 	EArabicDammatan = 28,
   279 	EArabicKasratan = 29,
   280 	EArabicFatha = 30,
   281 	EArabicDamma = 31,
   282 	EArabicKasra = 32,
   283 	EArabicShadda = 33,
   284 	EArabicSukun = 34,
   285 	ECombineBelowLeftAttached = 200,
   286 	ECombineBelowAttached = 202,
   287 	ECombineBelowRightAttached = 204,
   288 	ECombineLeftAttached = 208,
   289 	ECombineRightAttached = 210,
   290 	ECombineAboveLeftAttached = 212,
   291 	ECombineAboveAttached = 214,
   292 	ECombineAboveRightAttached = 216,
   293 	ECombineBelowLeft = 218,
   294 	ECombineBelow = 220,
   295 	ECombineBelowRight = 222,
   296 	ECombineLeft = 224,
   297 	ECombineRight = 226,
   298 	ECombineAboveLeft = 228,
   299 	ECombineAbove = 230,
   300 	ECombineAboveRight = 232
   301 	};
   302 
   303 
   304 /**
   305  This method is called to attach (by adjusing its bounding box) the current end
   306  glyph in the output array of iParam to the base glyph bounding box based on
   307  the Unicode combining class of the character.
   308 @param aGss
   309  The general input/output glyph selection data for the routine.
   310 @param aGss.iOutput
   311  Input: Glyph cluster with last glyph an actual combining character. Output:
   312  Bounding box of last glyph adjusted according to char combining class.
   313 @param aFirstDiacritic
   314  Which character in the output array to treat as the first diacritic of the
   315  cluster. Usually 1, but can be more if the base class is a ligature.
   316 */
   317 void TGlyphSelectionState::CombineLastGlyphToBase(const TRect& aBase, TInt aFirstDiacritic)
   318 	{
   319 	// Get the bounds of all the base characters.
   320 	TRect base = aBase;
   321 	int last = iParam.iOutputGlyphs-1;
   322 	for (int i = aFirstDiacritic; i < last; i++)
   323 		base.BoundingRect(iParam.iOutput[i].iBounds);
   324 
   325 	// Calculate the attachment points.
   326 	TRect& r = iParam.iOutput[last].iBounds;
   327 	int w = r.Width();
   328 	int h = r.Height();
   329 	int t = r.iTl.iY;
   330 	int l = r.iTl.iX;
   331 	int left = base.iTl.iX;
   332 	int center = base.iTl.iX + (base.Width() - w) / 2;
   333 	int right = base.iBr.iX - w;
   334 	int below = base.iBr.iY;
   335 	int above = base.iTl.iY - h;
   336 	int left_of = left - w;
   337 	int right_of = right + w;
   338 	int xGap = 1;
   339 	int yGap = iFont->HeightInPixels()/10;
   340 	
   341 	// Select attachment based on combining class.
   342 	switch (iCombCls)
   343 		{
   344 		case ECombineBelowLeftAttached:
   345 			t = below;
   346 			l = left;
   347 			break;
   348 		case ECombineBelowAttached:
   349 			t = below;
   350 			l = center;
   351 			break;
   352 		case ECombineBelowRightAttached:
   353 			t = below;
   354 			l = right;
   355 			break;
   356 		case ECombineLeftAttached:
   357 			l = left_of;
   358 			break;
   359 		case ECombineRightAttached:
   360 			l = right_of;
   361 			break;
   362 		case ECombineAboveLeftAttached:
   363 			t = above;
   364 			l = left;
   365 			break;
   366 		case ECombineAboveAttached:
   367 			t = above;
   368 			l = center;
   369 			break;
   370 		case ECombineAboveRightAttached:
   371 			t = above;
   372 			l = right;
   373 			break;
   374 		case ECombineBelowLeft:
   375 			t = below + yGap;
   376 			l = left;
   377 			break;
   378 		case ECombineBelow:
   379 		case EArabicKasratan:
   380 		case EArabicKasra:
   381 			t = below + yGap;
   382 			l = center;
   383 			break;
   384 		case ECombineBelowRight:
   385 			t = below + yGap;
   386 			l = right;
   387 			break;
   388 		case ECombineLeft:
   389 			l = left_of - xGap;
   390 			break;
   391 		case ECombineRight:
   392 			l = right_of + xGap;
   393 			break;
   394 		case ECombineAboveLeft:
   395 			t = above - yGap;
   396 			l = left;
   397 			break;
   398 		case ECombineAbove:
   399 		case EArabicFathatan:
   400 		case EArabicDammatan:
   401 		case EArabicFatha:
   402 		case EArabicDamma:
   403 		case EArabicShadda:
   404 		case EArabicSukun:
   405 			t = above - yGap;
   406 			l = center;
   407 			break;
   408 		case ECombineAboveRight:
   409 			t = above - yGap;
   410 			l = right;
   411 			break;
   412 		default:
   413 			l = center;
   414 			break;
   415 		}
   416 
   417 	// Adjust the bounding box of the last glyph to fix position
   418 	// based on the characters combining class. For speed, do directly.
   419 	// r.SetRect(l,t,l + w,t + h);
   420 	r.iTl.iX = l;
   421 	r.iTl.iY = t;
   422 	r.iBr.iX = l+w;
   423 	r.iBr.iY = t+h;
   424 	}
   425 
   426 
   427 TBool TGlyphSelectionState::AppendGlyphToCluster(TUint aCode)
   428 /**
   429  This common method is used by glyph selector classes to add a glyph to
   430  the end of the aGss.iParam output field filling in all the glyph info 
   431  needed.
   432 @param aCode
   433  The Unicode character for which a glyph should be appended.
   434 @param aGss
   435  The general input/output glyph selection data for the routine. 
   436 @return TBool
   437  ETrue when successful, EFalse when failure occurs e..g no char data, overflow
   438 */
   439 	{
   440 	// Setup reference to next free glyph record we need to update.
   441 	GDI_ASSERT_DEBUG(iParam.iOutputGlyphs < CFont::TPositionParam::EMaxOutputGlyphs, 
   442 		EGdiPanic_InvalidInputParam);
   443 	   
   444 	CFont::TPositionParam::TOutput* output = iParam.iOutput+iParam.iOutputGlyphs;
   445 
   446 	// Retrieve the glyph details from the Font. Essential to proceed, abort
   447 	// if not available.
   448 	TOpenFontCharMetrics metrics;
   449 	if (iFont->GetCharacterData(aCode, metrics, output->iBitmap, 
   450 		output->iBitmapSize) == CFont::ENoCharacterData)
   451 		return EFalse;
   452 
   453 	// Set code point of glyph in output record.
   454 	output->iCode = aCode;
   455 	
   456 	// Set the glyph's bounds in the output record and record pen advancement.
   457 	if (iParam.iDirection == CFont::EVertical)
   458 		{
   459 		metrics.GetVertBounds(output->iBounds);
   460 		iAdvance.iHeight = Max(iAdvance.iHeight, metrics.VertAdvance());
   461 		}
   462 	else
   463 		{
   464 		metrics.GetHorizBounds(output->iBounds);
   465 		iAdvance.iWidth = Max(iAdvance.iWidth, metrics.HorizAdvance());
   466 		}
   467 
   468 	// Next adjust the glyph's bounding box to offset it from the pen
   469 	// position (origin of drawing). For speed increment attributes directly.
   470 	// output->iBounds.Move(aGss.iParam.iPen);
   471 	output->iBounds.iTl.iX += iParam.iPen.iX;
   472 	output->iBounds.iBr.iX += iParam.iPen.iX;
   473 	output->iBounds.iTl.iY += iParam.iPen.iY;
   474 	output->iBounds.iBr.iY += iParam.iPen.iY;
   475 	
   476 	// Before we exit with success, increment the glyph array counter.
   477 	// for the new glyph we've added here.
   478 	iParam.iOutputGlyphs++;
   479 	return ETrue;
   480 	}
   481 
   482 
   483 // 
   484 //
   485 // GlyphSelector_SoftHyphen Class definition
   486 //
   487 //
   488 
   489 TBool GlyphSelector_SoftHyphen::Process(TGlyphSelectionState& aGss, RShapeInfo&) 
   490 /**
   491 @see GlyphSelUtils 
   492  See this class for the method description.
   493 */
   494 	{
   495  	aGss.iText.Next();
   496 	if (!aGss.iText.AtEnd())
   497 		{
   498 		// Here we skip & don't output hyphen since its not at the end a line.
   499 		aGss.iPen = TGlyphSelectionState::EPenAdvance_No;
   500 		}
   501 	else
   502 		{
   503 		// If we reach here we must output hyphen.
   504 		if (!aGss.AppendGlyphToCluster(KLatinGlyph_SoftHyphen))
   505 			return EFalse;
   506 		
   507 		aGss.iPen = TGlyphSelectionState::EPenAdvance_Yes;
   508 		}
   509 
   510 	// Logic to determine if we are now at the end of the glyph cluster.
   511 	// Default logic, based on whether a combining mark follows or not.
   512 	aGss.iClusterState = 
   513 		(!aGss.iText.AtEnd() &&
   514 			((aGss.iText.Get().GetCategory() & 0xF0) == TChar::EMarkGroup)) ?
   515 			TGlyphSelectionState::EGClusterNotComplete : TGlyphSelectionState::EGClusterComplete;
   516 
   517 	return ETrue;
   518 	}
   519 
   520 
   521 // 
   522 //
   523 // GlyphSelector_Default Class definition
   524 //
   525 //
   526 
   527 
   528 TBool GlyphSelector_Default::Process(TGlyphSelectionState& aGss, RShapeInfo&) 
   529 /**
   530 @see GlyphSelUtils 
   531  See this class for the method description.
   532 */
   533 	{
   534 	
   535 	// In this method we always output the glyph.
   536 	if (!aGss.AppendGlyphToCluster(aGss.iText.GetThenNext()))
   537 		return EFalse;
   538 
   539 	// Adjust glyph's bounds further to position this character if it is a
   540 	// combining mark
   541 	if (aGss.IsCombiningClass())
   542 		{
   543 		aGss.iPen = TGlyphSelectionState::EPenAdvance_No;
   544 		
   545 		TRect baseBounds(aGss.iParam.iOutput[0].iBounds);
   546 		// Get first character in this glyph cluster. In this default process function, the iCode should 
   547 		// be Unicode Point Code.  
   548 		TChar startChar = TChar(aGss.iParam.iOutput[0].iCode);
   549 		// Character index in the output array to treat as the first diacritic of the
   550 		// cluster. It will be used as first character for combine to. usually 1, but when
   551 		// the cluster starts with a combining mark, it should be set to 0.  
   552 		TInt indexOfFirstCombining = 1; 
   553 		TInt startCharCat = startChar.GetCategory() & 0xF0;
   554 		
   555 		// if the first character in this cluster is a combining mark or a graphically empty character, 
   556 		// (such as a space Character0x0020), a fake bound, formed from the Ascent of the font, will be 
   557 		// used for combining
   558 		if ((startCharCat == TChar::EMarkGroup) || baseBounds.Size() == TSize(0,0)) 
   559 			{
   560 			// Determine the height of the combining glyph.
   561 			TInt glyphHeight = 0;
   562 			if (aGss.iParam.iOutputGlyphs == 1)
   563 				{
   564 				glyphHeight = aGss.iParam.iOutput[0].iBitmapSize.iHeight;
   565 				}
   566 			else
   567 				{
   568 				glyphHeight = aGss.iParam.iOutput[1].iBitmapSize.iHeight;
   569 				}
   570 			// 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.
   571 			baseBounds.iTl.iY = aGss.iParam.iPen.iY - aGss.iFont->AscentInPixels() + glyphHeight; //modest ascender
   572 			baseBounds.iBr.iY = aGss.iParam.iPen.iY; //No descender
   573 			}
   574 		
   575 		if (startCharCat == TChar::EMarkGroup)
   576 			indexOfFirstCombining = 0;
   577 								
   578 		aGss.CombineLastGlyphToBase(baseBounds, indexOfFirstCombining);
   579 		aGss.iGlyphPostCombine = TGlyphSelectionState::EGPostCombine_Yes;
   580 		}	
   581 	else
   582 		aGss.iPen = TGlyphSelectionState::EPenAdvance_Yes;
   583 
   584 	// Logic to determine if we are now at the end of the glyph cluster.
   585 	// Default logic, based on whether a combining mark follows or not.
   586 	aGss.iClusterState = 
   587 		(!aGss.iText.AtEnd() &&
   588 		 ((aGss.iText.Get().GetCategory() & 0xF0) == TChar::EMarkGroup)) ?
   589 			TGlyphSelectionState::EGClusterNotComplete : TGlyphSelectionState::EGClusterComplete;
   590 
   591 	return ETrue;
   592 	}
   593