svgtopt/SVG/SVGImpl/src/SVGPathDataParser.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:20:46 +0100
branchRCL_3
changeset 18 1902ade171ab
parent 0 d46562c3d99d
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201026 Kit: 201035

/*
* Copyright (c) 2003 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:  SVG Implementation source file
 *
*/


#include "SVGPathDataParser.h"
#include "SVGPointLexer.h"

#include "GfxFloatFixPt.h"
#include "GfxAffineTransform.h"
#include "GfxFlatteningPathIterator.h"


// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
void TSvgPathDataParser::ParsePathData( const TDesC& aData,
                                          CGfxGeneralPath* aPath )
    {

		TRAPD( error, ParsePathDataL( aData, aPath ) );
		if ( error != KErrNone )
		     {
			 // error processing
			 return;
	         }
    }

// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
void TSvgPathDataParser::ParsePathDataL( const TDesC& aData,
                                           CGfxGeneralPath* aPath )
    {
    TUint8 lPrevCom = '\0';
	TFloatFixPt xy[6];
	TInt32 rslt = ETrue;
	TUint8 com;
    TUint8 lPrevCom2 = '\0';

    TSvgPointLexer svgplex( aData );

    TFloatFixPt KZero;
    TFloatFixPt KMinVal = 0.01f;
    TFloatFixPt KOne( KFloatFixOne );

    while ( svgplex.GetCommand ( com ) )
        {

			// Reset for command procesing
			if( com == '\0' )
				{
				if( lPrevCom == 'z' ||lPrevCom == 'Z' )
					{
					aPath->Reset();
					break ;
					}
				// As per 1.2  spec 8.3.2 The "moveto" commands
	            // If a 'moveto' is followed by multiple pairs of coordinates without explicit commands,
	            // the subsequent pairs shall be treated as implicit 'lineto' commands.

				else if( lPrevCom == 'm')
					{
					com = 'l';
					}
				else if( lPrevCom == 'M' )
					{
					com = 'L';
					}
				else
					{
					com = lPrevCom;
					}
				}
			else
				{
				lPrevCom = com;
				}

				switch ( com )
					{
					case 'M':
							rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (2) );
							if (rslt && lPrevCom2 == 'M')
								{
									//AJD do we want to remove this
									// i.e. d="M 20 20 M 10 10 L 15 15
									// has a bounding box of 5,5,10,10 or 10,10,10,10
									//aPath->RemoveLastPathCommand();
								}
							if(rslt)
                                {
                                aPath->MoveToL( xy[0], xy[1], ETrue );
                                }
                            break;
                    case 'm':
                            rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (2) );
                            // added to change values from 0.0 to 0.01f if both the co-oridnates
                            // are having value 0.0. 
                            // fix for bug "CGSR-78X9V2"
                            if(xy[0] == KZero && xy[1] == KZero)
                               {
                            	 xy[0] = KMinVal;
                            	 xy[1] = KMinVal;
                               }
                            if(rslt)
                               {
                               aPath->MoveToL( xy[0], xy[1], EFalse );
                               }
                            
							break;
					case 'L':
							rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (2) );
							if(rslt)
								{
								aPath->LineToL( xy[0], xy[1], ETrue );
								}
							break;
					case 'H':
							rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (1) );
							if(rslt)
								{
								aPath->HorizonToL( xy[0], ETrue );
								}
							break;
					case 'V':
							rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (1) );
							if(rslt)
                                {
                                aPath->VertToL( xy[0], ETrue );
                                }
                            break;
                    case 'l':
                            rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (2) );
                            // added to change values from 0.0 to 0.01f if both the co-oridnates
                            // are having value 0.0. 
                            // fix for bug "CGSR-78X9V2"
                            if(xy[0] == KZero && xy[1] == KZero)
                               {
                               xy[0] = KMinVal;
                               xy[1] = KMinVal;
                               }
                           	if(rslt)
							   {
							   aPath->LineToL( xy[0], xy[1], EFalse );
							   }
						    break;
                    case 'h':
                            rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (1) );
                            // added to change values from 0.0 to 0.01f if both the co-oridnates
                            // are having value 0.0. 
                            // fix for bug "CGSR-78X9V2"
                            if(xy[0] == KZero && xy[1] == KZero)
                               {
                               xy[0] = KMinVal;
                               xy[1] = KMinVal;
                               }
                            if(rslt)
                               {
                               aPath->HorizonToL( xy[0], EFalse );
                               }
                             
                            break;
                    case 'v':
                            rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (1) );
                            // added to change values from 0.0 to 0.01f if both the co-oridnates
                            // are having value 0.0. 
                            // fix for bug "CGSR-78X9V2"
                            if(xy[0] == KZero && xy[1] == KZero)
                               {
                               xy[0] = KMinVal;
                               xy[1] = KMinVal;
                               }
                            if(rslt)
                               {
                               aPath->VertToL( xy[0], EFalse );
                               }
                            
							break;
					case 'C':
							rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (6) );
							if( rslt )
								{
								aPath->CubicToL( xy[0],
										 xy[1],
										 xy[2],
										 xy[3],
										 xy[4],
										 xy[5],
										 ETrue );
								}
							break;
					case 'c':
							rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (6) );
							if(rslt)
								{
								aPath->CubicToL( xy[0],
												 xy[1],
												 xy[2],
												 xy[3],
												 xy[4],
												 xy[5],
												 EFalse );
								}
							break;
					case 'S':
							rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (4) );
							if(rslt)
								{
								aPath->ShortCubicToL( xy[0],
													  xy[1],
													  xy[2],
													  xy[3],
													  ETrue );
								}
							break;
					case 's':
							rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (4) );
							if(rslt)
								{
								aPath->ShortCubicToL( xy[0],
													  xy[1],
													  xy[2],
													  xy[3],
													  EFalse );
								}
							break;
					case 'Q':
							rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (4) );
							if( rslt )
								{
								aPath->QuadToL( xy[0],
												  xy[1],
												  xy[2],
												  xy[3],
												ETrue );
								}
							break;
					case 'T':
							rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (2) );
							if(rslt)
								{
								
								// If there is no previous command or if
								// the previous command was not a Q, q, T or t, the control point shall be current point.
								if ( lPrevCom2 !='Q' && lPrevCom2 != 'q' && lPrevCom2 != 'T'
								&& lPrevCom2 != 't' )
								   {
								   aPath->QuadToLWithNoControlPoint( xy[0], xy[1] );
							   }
								else
								   {
								   aPath->ShortQuadToL( xy[0], xy[1], ETrue );	
								   }
								}
							break;
					case 'q':
							rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (4) );
							if(rslt)
								{
								aPath->QuadToL( xy[0],
										  xy[1],
										  xy[2],
                                          xy[3],
                                        EFalse );
                                }
                            break;
                    case 't':
                            rslt = svgplex.GetNextWithNumOfPoints( xy ,TInt (2) );
                            if(xy[0] != KZero || xy[1] != KZero)
                                {
								if(rslt)
									{
									aPath->ShortQuadToL( xy[0], xy[1], EFalse );
									}
								}
							break;
					case 'Z':
					case 'z':
							aPath->ClosePathL();
							break;

					default:
							rslt=0;
							break;

					}// switch(lPrevCom)

				if( !rslt )
					{
					// This is a failing path case reset.
					aPath->Reset();
					break ;
					}

				lPrevCom2 = com;
        }
    svgplex.Cleanup();
    }

//
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
void TSvgPathDataParser::ParsePointDataL( const TDesC& aData,
                                           CGfxGeneralPath* aPath )
    {
    TSvgPointLexer svgplex( aData );
    TBool first = ETrue;
    while ( !svgplex.IsDone() )
        {
        TChar com;
        TFloatFixPt x, y;
        TInt32 rslt = svgplex.GetNextPoint( com, x, y );

        if ( rslt != KErrNone )
            {
            aPath->Reset();
            break;
            }
        if ( first )
            {
            aPath->MoveToL( x, y, ETrue );
            first = EFalse;
            }
        else
            {
            aPath->LineToL( x, y, ETrue );
            }
        }
    svgplex.Cleanup();
    }

//
// ---------------------------------------------------------------------------
//
// ---------------------------------------------------------------------------
TFloatFixPt TSvgPathDataParser::PathLengthL( CGfxGeneralPath* aPath )
    {
    CGfxFlatteningPathIterator* pitr;
    TGfxAffineTransform ident;
    pitr = CGfxFlatteningPathIterator::NewL( aPath, & ident, 3 );


    CleanupStack::PushL( pitr );

    TFloatFixPt len;
    TFloatFixPt lx, ly;
    TFloatFixPt dx, dy, int0x7f( KMAXFLOATFIX );
    TFloatFixPt tmpcoords[6];
    while ( !pitr->IsDone() )
        {
        switch ( pitr->CurrentSegment( tmpcoords ) )
            {
            case EGfxSegMoveTo:
                lx = tmpcoords[0];
                ly = tmpcoords[1];
                break;
            case EGfxSegLineTo:
                dx = lx - tmpcoords[0];
                dy = ly - tmpcoords[1];
                if ( dx > int0x7f || dy > int0x7f )
                    {
                    // calculate as integer
                    TInt32 tmplen, tx, ty;
                    tx = ( TInt32 ) dx;
                    ty = ( TInt32 ) dy;
	#ifdef SVG_FLOAT_BUILD
                    tmplen = TFloatFixPt::Sqrt(tx * tx + ty * ty);
	#else
                    tmplen = TFloatFixPt::FixedSqrtGeneral( tx * tx + ty * ty, 0 );
	#endif
                    len += TFloatFixPt( tmplen );
                    }
                else
                    {
                    // calculate as TFixPt
	
                    len += TFloatFixPt::Sqrt( dx * dx + dy * dy );
                    }
                lx = tmpcoords[0];
                ly = tmpcoords[1];
                break;
            case EGfxSegClose:
                ;// Not support multiple path length
                break;
            default:
                break;
            }
        pitr->NextL();
        }

    CleanupStack::PopAndDestroy( 1 );

    return len;
    }