/* --COPYRIGHT--,BSD
|
|
* Copyright (c) 2013, Texas Instruments Incorporated
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* * Neither the name of Texas Instruments Incorporated nor the names of
|
|
* its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
* --/COPYRIGHT--*/
|
|
|
|
|
|
#include "grlib.h"
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! \addtogroup line_api
|
|
//! @{
|
|
//
|
|
//*****************************************************************************
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! Draws a horizontal line.
|
|
//!
|
|
//! \param pContext is a pointer to the drawing context to use.
|
|
//! \param lX1 is the X coordinate of one end of the line.
|
|
//! \param lX2 is the X coordinate of the other end of the line.
|
|
//! \param lY is the Y coordinate of the line.
|
|
//!
|
|
//! This function draws a horizontal line, taking advantage of the fact that
|
|
//! the line is horizontal to draw it more efficiently. The clipping of the
|
|
//! horizontal line to the clipping rectangle is performed within this routine;
|
|
//! the display driver's horizontal line routine is used to perform the actual
|
|
//! line drawing.
|
|
//!
|
|
//! \return None.
|
|
//
|
|
//*****************************************************************************
|
|
void
|
|
GrLineDrawH(const tContext *pContext, long lX1, long lX2, long lY)
|
|
{
|
|
long lTemp;
|
|
|
|
//
|
|
// Check the arguments.
|
|
//
|
|
assert(pContext);
|
|
|
|
//
|
|
// If the Y coordinate of this line is not in the clipping region, then
|
|
// there is nothing to be done.
|
|
//
|
|
if((lY < pContext->sClipRegion.sYMin) ||
|
|
(lY > pContext->sClipRegion.sYMax))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Swap the X coordinates if the first is larger than the second.
|
|
//
|
|
if(lX1 > lX2)
|
|
{
|
|
lTemp = lX1;
|
|
lX1 = lX2;
|
|
lX2 = lTemp;
|
|
}
|
|
|
|
//
|
|
// If the entire line is outside the clipping region, then there is nothing
|
|
// to be done.
|
|
//
|
|
if((lX1 > pContext->sClipRegion.sXMax) ||
|
|
(lX2 < pContext->sClipRegion.sXMin))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Clip the starting coordinate to the left side of the clipping region if
|
|
// required.
|
|
//
|
|
if(lX1 < pContext->sClipRegion.sXMin)
|
|
{
|
|
lX1 = pContext->sClipRegion.sXMin;
|
|
}
|
|
|
|
//
|
|
// Clip the ending coordinate to the right side of the clipping region if
|
|
// required.
|
|
//
|
|
if(lX2 > pContext->sClipRegion.sXMax)
|
|
{
|
|
lX2 = pContext->sClipRegion.sXMax;
|
|
}
|
|
|
|
//
|
|
// Call the low level horizontal line drawing routine.
|
|
//
|
|
DpyLineDrawH(pContext->pDisplay, lX1, lX2, lY, pContext->ulForeground);
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! Draws a vertical line.
|
|
//!
|
|
//! \param pContext is a pointer to the drawing context to use.
|
|
//! \param lX is the X coordinate of the line.
|
|
//! \param lY1 is the Y coordinate of one end of the line.
|
|
//! \param lY2 is the Y coordinate of the other end of the line.
|
|
//!
|
|
//! This function draws a vertical line, taking advantage of the fact that the
|
|
//! line is vertical to draw it more efficiently. The clipping of the vertical
|
|
//! line to the clipping rectangle is performed within this routine; the
|
|
//! display driver's vertical line routine is used to perform the actual line
|
|
//! drawing.
|
|
//!
|
|
//! \return None.
|
|
//
|
|
//*****************************************************************************
|
|
void
|
|
GrLineDrawV(const tContext *pContext, long lX, long lY1, long lY2)
|
|
{
|
|
long lTemp;
|
|
|
|
//
|
|
// Check the arguments.
|
|
//
|
|
assert(pContext);
|
|
|
|
//
|
|
// If the X coordinate of this line is not within the clipping region, then
|
|
// there is nothing to be done.
|
|
//
|
|
if((lX < pContext->sClipRegion.sXMin) ||
|
|
(lX > pContext->sClipRegion.sXMax))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Swap the Y coordinates if the first is larger than the second.
|
|
//
|
|
if(lY1 > lY2)
|
|
{
|
|
lTemp = lY1;
|
|
lY1 = lY2;
|
|
lY2 = lTemp;
|
|
}
|
|
|
|
//
|
|
// If the entire line is out of the clipping region, then there is nothing
|
|
// to be done.
|
|
//
|
|
if((lY1 > pContext->sClipRegion.sYMax) ||
|
|
(lY2 < pContext->sClipRegion.sYMin))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Clip the starting coordinate to the top side of the clipping region if
|
|
// required.
|
|
//
|
|
if(lY1 < pContext->sClipRegion.sYMin)
|
|
{
|
|
lY1 = pContext->sClipRegion.sYMin;
|
|
}
|
|
|
|
//
|
|
// Clip the ending coordinate to the bottom side of the clipping region if
|
|
// required.
|
|
//
|
|
if(lY2 > pContext->sClipRegion.sYMax)
|
|
{
|
|
lY2 = pContext->sClipRegion.sYMax;
|
|
}
|
|
|
|
//
|
|
// Call the low level vertical line drawing routine.
|
|
//
|
|
DpyLineDrawV(pContext->pDisplay, lX, lY1, lY2, pContext->ulForeground);
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! Computes the clipping code used by the Cohen-Sutherland clipping algorithm.
|
|
//!
|
|
//! \param pContext is a pointer to the drawing context to use.
|
|
//! \param lX is the X coordinate of the point.
|
|
//! \param lY is the Y coordinate of the point.
|
|
//!
|
|
//! This function computes the clipping code used by the Cohen-Sutherland
|
|
//! clipping algorithm. Clipping is performed by classifying the endpoints of
|
|
//! the line based on their relation to the clipping region; this determines
|
|
//! those relationships.
|
|
//!
|
|
//! \return Returns the clipping code.
|
|
//
|
|
//*****************************************************************************
|
|
static long
|
|
GrClipCodeGet(const tContext *pContext, long lX, long lY)
|
|
{
|
|
long lCode;
|
|
|
|
//
|
|
// Initialize the clipping code to zero.
|
|
//
|
|
lCode = 0;
|
|
|
|
//
|
|
// Set bit zero of the clipping code if the Y coordinate is above the
|
|
// clipping region.
|
|
//
|
|
if(lY < pContext->sClipRegion.sYMin)
|
|
{
|
|
lCode |= 1;
|
|
}
|
|
|
|
//
|
|
// Set bit one of the clipping code if the Y coordinate is below the
|
|
// clipping region.
|
|
//
|
|
if(lY > pContext->sClipRegion.sYMax)
|
|
{
|
|
lCode |= 2;
|
|
}
|
|
|
|
//
|
|
// Set bit two of the clipping code if the X coordinate is to the left of
|
|
// the clipping region.
|
|
//
|
|
if(lX < pContext->sClipRegion.sXMin)
|
|
{
|
|
lCode |= 4;
|
|
}
|
|
|
|
//
|
|
// Set bit three of the clipping code if the X coordinate is to the right
|
|
// of the clipping region.
|
|
//
|
|
if(lX > pContext->sClipRegion.sXMax)
|
|
{
|
|
lCode |= 8;
|
|
}
|
|
|
|
//
|
|
// Return the clipping code.
|
|
//
|
|
return(lCode);
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! Clips a line to the clipping region.
|
|
//!
|
|
//! \param pContext is a pointer to the drawing context to use.
|
|
//! \param plX1 is the X coordinate of the start of the line.
|
|
//! \param plY1 is the Y coordinate of the start of the line.
|
|
//! \param plX2 is the X coordinate of the end of the line.
|
|
//! \param plY2 is the Y coordinate of the end of the line.
|
|
//!
|
|
//! This function clips a line to the extents of the clipping region using the
|
|
//! Cohen-Sutherland clipping algorithm. The ends of the line are classified
|
|
//! based on their relation to the clipping region, and the codes are used to
|
|
//! either trivially accept a line (both end points within the clipping
|
|
//! region), trivially reject a line (both end points to one side of the
|
|
//! clipping region), or to adjust an endpoint one axis at a time to the edge
|
|
//! of the clipping region until the line can either be trivially accepted or
|
|
//! trivially rejected.
|
|
//!
|
|
//! The provided coordinates are modified such that they reside within the
|
|
//! extents of the clipping region if the line is not rejected. If it is
|
|
//! rejected, the coordinates may be modified during the process of attempting
|
|
//! to clip them.
|
|
//!
|
|
//! \return Returns one if the clipped line lies within the extent of the
|
|
//! clipping region and zero if it does not.
|
|
//
|
|
//*****************************************************************************
|
|
static long
|
|
GrLineClip(const tContext *pContext, long *plX1, long *plY1, long *plX2,
|
|
long *plY2)
|
|
{
|
|
long lCode, lCode1, lCode2, lX, lY;
|
|
|
|
//
|
|
// Compute the clipping codes for the two endpoints of the line.
|
|
//
|
|
lCode1 = GrClipCodeGet(pContext, *plX1, *plY1);
|
|
lCode2 = GrClipCodeGet(pContext, *plX2, *plY2);
|
|
|
|
//
|
|
// Loop forever. This loop will be explicitly broken out of when the line
|
|
// is either trivially accepted or trivially rejected.
|
|
//
|
|
while(1)
|
|
{
|
|
//
|
|
// If both codes are zero, then both points lie within the extent of
|
|
// the clipping region. In this case, trivally accept the line.
|
|
//
|
|
if((lCode1 == 0) && (lCode2 == 0))
|
|
{
|
|
return(1);
|
|
}
|
|
|
|
//
|
|
// If the intersection of the codes is non-zero, then the line lies
|
|
// entirely off one edge of the clipping region. In this case,
|
|
// trivally reject the line.
|
|
//
|
|
if((lCode1 & lCode2) != 0)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// Determine the end of the line to move. The first end of the line is
|
|
// moved until it is within the clipping region, and then the second
|
|
// end of the line is moved until it is also within the clipping
|
|
// region.
|
|
//
|
|
if(lCode1)
|
|
{
|
|
lCode = lCode1;
|
|
}
|
|
else
|
|
{
|
|
lCode = lCode2;
|
|
}
|
|
|
|
//
|
|
// See if this end of the line lies above the clipping region.
|
|
//
|
|
if(lCode & 1)
|
|
{
|
|
//
|
|
// Move this end of the line to the intersection of the line and
|
|
// the top of the clipping region.
|
|
//
|
|
lX = (*plX1 + (((*plX2 - *plX1) *
|
|
(pContext->sClipRegion.sYMin - *plY1)) /
|
|
(*plY2 - *plY1)));
|
|
lY = pContext->sClipRegion.sYMin;
|
|
}
|
|
|
|
//
|
|
// Otherwise, see if this end of the line lies below the clipping
|
|
// region.
|
|
//
|
|
else if(lCode & 2)
|
|
{
|
|
//
|
|
// Move this end of the line to the intersection of the line and
|
|
// the bottom of the clipping region.
|
|
//
|
|
lX = (*plX1 + (((*plX2 - *plX1) *
|
|
(pContext->sClipRegion.sYMax - *plY1)) /
|
|
(*plY2 - *plY1)));
|
|
lY = pContext->sClipRegion.sYMax;
|
|
}
|
|
|
|
//
|
|
// Otherwise, see if this end of the line lies to the left of the
|
|
// clipping region.
|
|
//
|
|
else if(lCode & 4)
|
|
{
|
|
//
|
|
// Move this end of the line to the intersection of the line and
|
|
// the left side of the clipping region.
|
|
//
|
|
lX = pContext->sClipRegion.sXMin;
|
|
lY = (*plY1 + (((*plY2 - *plY1) *
|
|
(pContext->sClipRegion.sXMin - *plX1)) /
|
|
(*plX2 - *plX1)));
|
|
}
|
|
|
|
//
|
|
// Otherwise, this end of the line lies to the right of the clipping
|
|
// region.
|
|
//
|
|
else
|
|
{
|
|
//
|
|
// Move this end of the line to the intersection of the line and
|
|
// the right side of the clipping region.
|
|
//
|
|
lX = pContext->sClipRegion.sXMax;
|
|
lY = (*plY1 + (((*plY2 - *plY1) *
|
|
(pContext->sClipRegion.sXMax - *plX1)) /
|
|
(*plX2 - *plX1)));
|
|
}
|
|
|
|
//
|
|
// See which end of the line just moved.
|
|
//
|
|
if(lCode1)
|
|
{
|
|
//
|
|
// Save the new coordinates for the start of the line.
|
|
//
|
|
*plX1 = lX;
|
|
*plY1 = lY;
|
|
|
|
//
|
|
// Recompute the clipping code for the start of the line.
|
|
//
|
|
lCode1 = GrClipCodeGet(pContext, lX, lY);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Save the new coordinates for the end of the line.
|
|
//
|
|
*plX2 = lX;
|
|
*plY2 = lY;
|
|
|
|
//
|
|
// Recompute the clipping code for the end of the line.
|
|
//
|
|
lCode2 = GrClipCodeGet(pContext, lX, lY);
|
|
}
|
|
}
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//! Draws a line.
|
|
//!
|
|
//! \param pContext is a pointer to the drawing context to use.
|
|
//! \param lX1 is the X coordinate of the start of the line.
|
|
//! \param lY1 is the Y coordinate of the start of the line.
|
|
//! \param lX2 is the X coordinate of the end of the line.
|
|
//! \param lY2 is the Y coordinate of the end of the line.
|
|
//!
|
|
//! This function draws a line, utilizing GrLineDrawH() and GrLineDrawV() to
|
|
//! draw the line as efficiently as possible. The line is clipped to the
|
|
//! clippping rectangle using the Cohen-Sutherland clipping algorithm, and then
|
|
//! scan converted using Bresenham's line drawing algorithm.
|
|
//!
|
|
//! \return None.
|
|
//
|
|
//*****************************************************************************
|
|
void
|
|
GrLineDraw(const tContext *pContext, long lX1, long lY1, long lX2, long lY2)
|
|
{
|
|
long lError, lDeltaX, lDeltaY, lYStep, bSteep;
|
|
|
|
//
|
|
// Check the arguments.
|
|
//
|
|
assert(pContext);
|
|
|
|
//
|
|
// See if this is a vertical line.
|
|
//
|
|
if(lX1 == lX2)
|
|
{
|
|
//
|
|
// It is more efficient to avoid Bresenham's algorithm when drawing a
|
|
// vertical line, so use the vertical line routine to draw this line.
|
|
//
|
|
GrLineDrawV(pContext, lX1, lY1, lY2);
|
|
|
|
//
|
|
// The line has ben drawn, so return.
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// See if this is a horizontal line.
|
|
//
|
|
if(lY1 == lY2)
|
|
{
|
|
//
|
|
// It is more efficient to avoid Bresenham's algorithm when drawing a
|
|
// horizontal line, so use the horizontal line routien to draw this
|
|
// line.
|
|
//
|
|
GrLineDrawH(pContext, lX1, lX2, lY1);
|
|
|
|
//
|
|
// The line has ben drawn, so return.
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Clip this line if necessary, and return without drawing anything if the
|
|
// line does not cross the clipping region.
|
|
//
|
|
if(GrLineClip(pContext, &lX1, &lY1, &lX2, &lY2) == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Determine if the line is steep. A steep line has more motion in the Y
|
|
// direction than the X direction.
|
|
//
|
|
if(((lY2 > lY1) ? (lY2 - lY1) : (lY1 - lY2)) >
|
|
((lX2 > lX1) ? (lX2 - lX1) : (lX1 - lX2)))
|
|
{
|
|
bSteep = 1;
|
|
}
|
|
else
|
|
{
|
|
bSteep = 0;
|
|
}
|
|
|
|
//
|
|
// If the line is steep, then swap the X and Y coordinates.
|
|
//
|
|
if(bSteep)
|
|
{
|
|
lError = lX1;
|
|
lX1 = lY1;
|
|
lY1 = lError;
|
|
lError = lX2;
|
|
lX2 = lY2;
|
|
lY2 = lError;
|
|
}
|
|
|
|
//
|
|
// If the starting X coordinate is larger than the ending X coordinate,
|
|
// then swap the start and end coordinates.
|
|
//
|
|
if(lX1 > lX2)
|
|
{
|
|
lError = lX1;
|
|
lX1 = lX2;
|
|
lX2 = lError;
|
|
lError = lY1;
|
|
lY1 = lY2;
|
|
lY2 = lError;
|
|
}
|
|
|
|
//
|
|
// Compute the difference between the start and end coordinates in each
|
|
// axis.
|
|
//
|
|
lDeltaX = lX2 - lX1;
|
|
lDeltaY = (lY2 > lY1) ? (lY2 - lY1) : (lY1 - lY2);
|
|
|
|
//
|
|
// Initialize the error term to negative half the X delta.
|
|
//
|
|
lError = -lDeltaX / 2;
|
|
|
|
//
|
|
// Determine the direction to step in the Y axis when required.
|
|
//
|
|
if(lY1 < lY2)
|
|
{
|
|
lYStep = 1;
|
|
}
|
|
else
|
|
{
|
|
lYStep = -1;
|
|
}
|
|
|
|
//
|
|
// Loop through all the points along the X axis of the line.
|
|
//
|
|
for(; lX1 <= lX2; lX1++)
|
|
{
|
|
//
|
|
// See if this is a steep line.
|
|
//
|
|
if(bSteep)
|
|
{
|
|
//
|
|
// Plot this point of the line, swapping the X and Y coordinates.
|
|
//
|
|
DpyPixelDraw(pContext->pDisplay, lY1, lX1, pContext->ulForeground);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Plot this point of the line, using the coordinates as is.
|
|
//
|
|
DpyPixelDraw(pContext->pDisplay, lX1, lY1, pContext->ulForeground);
|
|
}
|
|
|
|
//
|
|
// Increment the error term by the Y delta.
|
|
//
|
|
lError += lDeltaY;
|
|
|
|
//
|
|
// See if the error term is now greater than zero.
|
|
//
|
|
if(lError > 0)
|
|
{
|
|
//
|
|
// Take a step in the Y axis.
|
|
//
|
|
lY1 += lYStep;
|
|
|
|
//
|
|
// Decrement the error term by the X delta.
|
|
//
|
|
lError -= lDeltaX;
|
|
}
|
|
}
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
// Close the Doxygen group.
|
|
//! @}
|
|
//
|
|
//*****************************************************************************
|