graphicsdeviceinterface/gdi/sgdi/LINE.CPP
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 01:47:50 +0200
changeset 0 5d03bc08d59c
permissions -rw-r--r--
Revision: 201003 Kit: 201005

// Copyright (c) 1998-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:
//

#include <gdi.h>

#ifdef __ARMCC__
#pragma arm
#pragma O3
#pragma Otime
#endif

EXPORT_C TLinearDDA::TLinearDDA():
	iCount(0),
	iDifference(),
	iFinish(),
	iGradient(0),
	iInc(),
	iPos(),
	iStart(),
	iBoundingRect(),
	iBoundingRectSet(EFalse),
	iInsideX(EFalse),
	iInsideY(EFalse),
	iStatus(EComplete)
/** Constructs the default linear DDA.

No start or end point is defined for the line. */
	{}

 
EXPORT_C TLinearDDA::TLinearDDA(const TLinearDDA& aLine):
	iCount(aLine.iCount),
	iDifference(aLine.iDifference),
	iFinish(aLine.iFinish),
	iGradient(aLine.iGradient),
	iInc(aLine.iInc),
	iPos(aLine.iPos),
	iStart(aLine.iStart),
	iBoundingRect(aLine.iBoundingRect),
	iBoundingRectSet(aLine.iBoundingRectSet),
	iInsideX(aLine.iInsideX),
	iInsideY(aLine.iInsideY),
	iStatus(aLine.iStatus)
/** Copy constructs a linear DDA from the specified linear DDA.

@param aLine The linear DDA to be copied. */
	{}

 
EXPORT_C void TLinearDDA::Construct(const TPoint& aStart,const TPoint& aFinish,TLineMode aMode)
/** Constructs a linear DDA, setting the start and end points of the line.

@param aStart The start point of the line. 
@param aFinish The end point of the line. 
@param aMode The mode of the line; defaults to centred. */
	{
	iStart=aStart;
	iFinish=aFinish;
	iDifference=(iFinish-iStart).AsSize();
	iDifference.iWidth=Abs(iDifference.iWidth);
	iDifference.iHeight=Abs(iDifference.iHeight);
	iInc.iX=(iStart.iX>iFinish.iX)?-1:1;
	iInc.iY=(iStart.iY>iFinish.iY)?-1:1;
	if(iDifference.iWidth)
		iGradient=(iFinish.iY-iStart.iY)/(iFinish.iX-iStart.iX);
	iPos=iStart;
	if(!iGradient)
		iCount=iDifference.iWidth;
	else
		iCount=iDifference.iHeight;
	if(aMode==ECenter)
		iCount>>=1;
	else
		if(iCount)
			iCount--;
	iStatus=EInitialised;
	if(aStart==aFinish)
		iStatus=EComplete;
	iBoundingRectSet=EFalse;
	iInsideX = EFalse;
	iInsideY = EFalse;
	}

 
EXPORT_C TBool TLinearDDA::SingleStep(TPoint& aPosition)
/** Gets the pixel co-ordinates of the next pixel on the pixel line.

The function is called repeatedly until the whole line has been traversed or, 
if JumpToRect() has been called, until the part of the line inside the rectangle 
has been traversed. Note that, for performance reasons, JumpToRect() may fail 
to detect the intersection of the line with the rectangle accurately and 
SingleStep() may return more points than strictly necessary.

@param aPosition On entry to the first call, this can be a reference to any 
point. On return from the first call, this is the position of the first pixel 
in the line, as specified during construction of this object. On return from 
subsequent calls, this is the position of subsequent pixels in the line, as 
calculated by the function. On return from the final call, this is the position 
of the last pixel in the line, as specified during construction of this object.
@return ETrue, when the position of the last pixel is returned; EFalse, 
otherwise. */
    {
    switch (iStatus)
        {
        case EInitialised:
            aPosition = iStart;
            iStatus = ECurrent;
            return EFalse;
        case ECurrent:
            if (iDifference.iHeight == 0) // horizontal line
                {
                iPos.iX += iInc.iX;
                if (iPos.iX == iFinish.iX)
                    {
                    iStatus = EComplete;
                    }
                }
            else if (iDifference.iWidth == 0) // vertical line
                {
                iPos.iY += iInc.iY;
                if (iPos.iY == iFinish.iY)
                    {
                    iStatus = EComplete;
                    }
                }
            else // diagonal stripes
                {
                if (!iGradient)
                    {
                    iCount -= iDifference.iHeight;
                    iPos.iX += iInc.iX;
                    if (iCount < 0)
                        {
                        iCount += iDifference.iWidth;
                        iPos.iY += iInc.iY;
                        }
                    }
                else
                    {
                    iCount -= iDifference.iWidth;
                    iPos.iY += iInc.iY;
                    if (iCount < 0)
                        {
                        iCount += iDifference.iHeight;
                        iPos.iX += iInc.iX;
                        }
                    }
                if ((iPos.iX == iFinish.iX) && (iPos.iY == iFinish.iY))
                    {
                    iStatus = EComplete;
                    }
                }
            // common
            aPosition = iPos;
            if (iStatus == EComplete)
                {
                return ETrue;
                }
            if(iBoundingRectSet)
                {
                if (iPos.iX >= iBoundingRect.iTl.iX && iPos.iX < iBoundingRect.iBr.iX)
                    iInsideX = ETrue;
                else
                    if (iInsideX)
                        {
                        iStatus=EComplete;
                        return(ETrue);
                        }
                if (iPos.iY >= iBoundingRect.iTl.iY && iPos.iY < iBoundingRect.iBr.iY)
                    iInsideY = ETrue;
                else
                    if (iInsideY)
                        {
                        iStatus=EComplete;
                        return(ETrue);
                        }
                }
            return EFalse;
        default:
            aPosition = iFinish;
            return ETrue;
        }
    }
 
EXPORT_C TBool TLinearDDA::NextStep(TPoint& aPosition)
/** Gets the pixel co-ordinates of the start of the next scan line.

The best line that joins the start and end points is formed from all the scan 
lines returned by this function.

The function is called repeatedly until the start position of all scanlines 
has been returned.

The start and end points passed to the constructor of this object define the 
boundaries of the line. Successive scan lines move from the start point to 
the end point.

@param aPosition On entry to the first call, this can be a reference to any 
point. On return from the first call, this is the position of the pixel that 
defines the leftmost position of the first scan line. On return from subsequent 
calls, this is the position of the pixel that defines the leftmost position 
of the next scan line. On return from the final call, this is the position 
of the last pixel in the line, as specified during construction. 
@return ETrue, when the position of the last pixel is returned; EFalse, 
otherwise. */
    {
    if (!iDifference.iHeight) // horizontal line
        {
        iPos = iFinish;
        iStatus = EComplete;
        aPosition = iFinish;
        return ETrue;
        }
    if (!iDifference.iWidth || iGradient || (iStatus != ECurrent))
        {
        return SingleStep(aPosition);
        }
    // !iGradient && (iStatus != EInitialised)
    if(iBoundingRectSet)
        { // slower version
        while ((iCount - iDifference.iHeight) >= 0)
            {
            if (SingleStep(aPosition))
                return ETrue;
            }
        return SingleStep(aPosition);
        }
    // faster version avoids function calls
    TBool lastLoop = EFalse;
    do {
        if ((iCount - iDifference.iHeight) < 0)
            {
            lastLoop = ETrue;
            }
        iCount -= iDifference.iHeight;
        iPos.iX += iInc.iX;
        if (iCount < 0)
            {
            iCount += iDifference.iWidth;
            iPos.iY += iInc.iY;
            }

        if ((iPos.iX == iFinish.iX) && (iPos.iY == iFinish.iY))
            {
            aPosition = iFinish;
            iStatus = EComplete;
            return ETrue;
            }
        }
        while (!lastLoop);
    aPosition = iPos;
    return EFalse;
    }
 
EXPORT_C TBool TLinearDDA::SingleScanline(TPoint& aStartPosition,TPoint& aEndPosition)
/** Gets the start and end pixel co-ordinates that define the next scan line.

The best line that joins the start and end points is formed from all the scan 
lines returned by this function.

The function is called repeatedly until the position of all scanlines has 
been returned.

The start and end points passed to the constructor of this object define the 
boundaries of the line. Successive scan lines move from the start point to 
the end point.

@param aStartPosition On entry to the first call, this can be a reference 
to any point. On return from the first call, this is the position of the pixel 
that defines the leftmost position of the first scan line. On return from 
subsequent calls, this is the position of the pixel that defines the leftmost 
position of the next scan line. On return from the final call, either this 
or aEndPosition is set to the end point, as specified during construction.
@param aEndPosition On entry to the first call, this can be a reference to 
any point. On return from the first call, this is the position of the pixel 
that defines the rightmost position of the first scan line. On return from 
subsequent calls, this is the position of the pixel that defines the rightmost 
position of the next scan line. On return from the final call, either this 
or aStartPosition is set to the end point, as specified during construction.
@return ETrue, when the position of the last scan line includes the end point; 
EFalse, otherwise. */
    {
    TBool done=EFalse;
    if(iDifference.iHeight==0)
        {
        aStartPosition=iStart;
        aEndPosition=iFinish;
        return(ETrue);
        }
    if(iDifference.iWidth==0 || iGradient)
        {
        done=SingleStep(aStartPosition);
        aEndPosition=aStartPosition;
        return(done);
        }
    // !iGradient
    done=SingleStep(aStartPosition);
    aEndPosition=aStartPosition;
    while(iCount-iDifference.iHeight>=0 && !done)
        {
        iCount -= iDifference.iHeight;
        iPos.iX += iInc.iX;
        if (iCount < 0)
            {
            iCount += iDifference.iWidth;
            iPos.iY += iInc.iY;
            }

        if ((iPos.iX == iFinish.iX) && (iPos.iY == iFinish.iY))
            {
            iStatus = EComplete;
            done = ETrue;
            }
        }
    aEndPosition = iPos;
    return done;
    }
 
EXPORT_C void TLinearDDA::JumpToRect(const TRect& aRect)
/** Jumps to start of a clipping rectangle.

This will accelerate the linear DDA to the vicinity of the specified rectangle. 
It is NOT guaranteed to reach the rectangle, but will reduce co-ordinates 
that are 1000's out to co-ordinates that are 10's out. Because of this, failure 
to intersect the rectangle may not be detected. If it is, or the line has 
not been constructed or has been run to completion, then a subsequent call 
to the stepping functions returns ETrue.

@param aRect The rectangle to be jumped to. */
	{
	if(aRect.IsEmpty() || iStatus!=EInitialised) return;
	iBoundingRect=aRect;
	iBoundingRectSet=ETrue;

	TInt nearestx = 0;
	if (iStart.iX < aRect.iTl.iX)
		nearestx = aRect.iTl.iX;
	else if (iStart.iX >= aRect.iBr.iX)
		nearestx = aRect.iBr.iX;
	else
		iInsideX = ETrue;
	TInt nearesty = 0;
	if (iStart.iY < aRect.iTl.iY)
		nearesty = aRect.iTl.iY;
	else if (iStart.iY >= aRect.iBr.iY)
		nearesty = aRect.iBr.iY;
	else
		iInsideY = ETrue;
	if (iInsideX && iInsideY)
		return;

	TInt dummy;
	if(!iGradient)
		{
		if (iInsideX)
			return;
		JumpToXCoord(nearestx,dummy);
		}
	else
		{
		if (iInsideY)
			return;
		JumpToYCoord(dummy,nearesty);
		}
	}

 
EXPORT_C void TLinearDDA::JumpToXCoord(const TInt aXCoord,TInt& aYCoord)
/** Jumps to x co-ordinate.

The other co-ordinate of the intersection is returned through a reference 
argument. After a jump call, the line is ready to continue through calls to 
the stepping functions.

This function accelerates the Linear DDA stepping functions (e.g. SingleStep()) 
making them return EFalse when they reach the specified co-ordinate. If the 
line does not cross the co-ordinate, has not been constructed, has been run 
to completion or the intersection is the end point of the line then the stepping 
functions will return ETrue.

@param aXCoord x co-ordinate to jump to 
@param aYCoord On return, this parameter holds the y co-ordinate which corresponds 
to the specified x co-ordinate */
	{
	if(iStatus==EComplete) return; // not constructed
	if((iStart.iX<aXCoord && iFinish.iX<aXCoord) || (iStart.iX>aXCoord && iFinish.iX>aXCoord))
		return; // no intersection
	aYCoord=iStart.iY;
	if(iStart.iX==aXCoord) return; // trivial first intersection
	iStatus=ECurrent;
	if(iDifference.iHeight==0) // horizontal line
		iPos.iX=aXCoord;
	else
		{
		if(!iGradient)
			{
			TInt64 numsteps=Abs(aXCoord-iPos.iX);
			TInt64 tempcount=TInt64(iCount)-(TInt64(iDifference.iHeight)*numsteps);
			numsteps=Abs(tempcount/iDifference.iWidth);
			tempcount+=numsteps*iDifference.iWidth;
			while(tempcount<0)
				{
				tempcount+=iDifference.iWidth;
				numsteps++;
				}
			iCount = I64INT(tempcount);
			iPos.iY += (iInc.iY * I64INT(numsteps));
			iPos.iX=aXCoord;
			aYCoord=iPos.iY;
			}
		else
			{
			while(iPos.iX!=aXCoord)
				{
				iCount-=iDifference.iWidth;
				if(iCount<0)
					{
					iCount+=iDifference.iHeight;
					iPos.iX+=iInc.iX;
					}
				iPos.iY+=iInc.iY;
				}
			aYCoord=iPos.iY;
			}
		}
    if ((iPos.iX == iFinish.iX) && (iPos.iY == iFinish.iY))
        {
        iStatus=EComplete;
        }
	}

 
EXPORT_C void TLinearDDA::JumpToYCoord(TInt& aXCoord,const TInt aYCoord)
/** Jumps to a y co-ordinate.

The other co-ordinate of the intersection is returned through a reference 
argument. After a jump call, the line is ready to continue through calls to 
the stepping functions.

This function accelerates the Linear DDA stepping functions (e.g. SingleStep()) 
making them return EFalse when they reach the specified co-ordinate. If the 
line does not cross the co-ordinate, has not been constructed, has been run 
to completion or the intersection is the end point of the line then they will 
return ETrue. 

@param aXCoord On return, this parameter holds the x co-ordinate which corresponds 
to the specified y co-ordinate. 
@param aYCoord y co-ordinate to jump to */
	{
	if(iStatus==EComplete) return; // not constructed
	if((iStart.iY<aYCoord && iFinish.iY<aYCoord) || (iStart.iY>aYCoord && iFinish.iY>aYCoord))
		return; // no intersection
	aXCoord=iStart.iX;
	if(iStart.iY==aYCoord) return; // trivial first intersection
	iStatus=ECurrent;
	if(iDifference.iWidth==0) // vertical line
		iPos.iY=aYCoord;
	else
		{
		if(!iGradient)
			{
			while(iPos.iY!=aYCoord)
				{
				iCount-=iDifference.iHeight;
				if(iCount<0)
					{
					iCount+=iDifference.iWidth;
					iPos.iY+=iInc.iY;
					}
				iPos.iX+=iInc.iX;
				}
			aXCoord=iPos.iX;
			}
		else
			{
			TInt64 numsteps=Abs(aYCoord-iPos.iY);
			TInt64 tempcount=TInt64(iCount)-(TInt64(iDifference.iWidth)*numsteps);
			numsteps=Abs(tempcount/iDifference.iHeight);
			tempcount+=numsteps*iDifference.iHeight;
			while (tempcount<0)
				{
				tempcount+=iDifference.iHeight;
				numsteps++;
				}
			iCount = I64INT(tempcount);
			iPos.iX += (iInc.iX * I64INT(numsteps));
			iPos.iY=aYCoord;
			aXCoord=iPos.iX;
			}
		}
    if ((iPos.iX == iFinish.iX) && (iPos.iY == iFinish.iY))
        {
        iStatus=EComplete;
        }
	}

void TLinearDDA::UpdatePosition()
	{
	}

EXPORT_C void TLinearDDA::JumpToXCoord2(TInt aXCoord,TInt& aYCoord)
/**
Jumps to x co-ordinate.

This works in the same way as TLinearDDA::JumpToXCoord except that it make sure
that using the NextStep function does not return the same value twice.

@param aXCoord x co-ordinate to jump to
@param aYCoord On return, this parameter holds the y co-ordinate which corresponds
to the specified x co-ordinate
@see TLinearDDA::JumpToXCoord(TInt, TInt&)
*/
	{
	JumpToXCoord(aXCoord,aYCoord);
	iStatus=ECurrent;
	}

EXPORT_C void TLinearDDA::JumpToYCoord2(TInt& aXCoord,TInt aYCoord)
/**
Jumps to a y co-ordinate.

This works in the same way as TLinearDDA::JumpToYCoord except that it make sure
that using the NextStep function does not return the same value twice.

@param aXCoord On return, this parameter holds the x co-ordinate which corresponds
to the specified y co-ordinate.
@param aYCoord y co-ordinate to jump to
@see TLinearDDA::JumpToYCoord(TInt&, TInt)
*/ 
	{
	JumpToYCoord(aXCoord,aYCoord);
	iStatus=ECurrent;
	}