| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630 |
- //*****************************************************************************
- //
- // line.c - Routines for drawing lines.
- //
- // Copyright (c) 2007-2010 Texas Instruments Incorporated. All rights reserved.
- // Software License Agreement
- //
- // Texas Instruments (TI) is supplying this software for use solely and
- // exclusively on TI's microcontroller products. The software is owned by
- // TI and/or its suppliers, and is protected under applicable copyright
- // laws. You may not combine this software with "viral" open-source
- // software in order to form a larger program.
- //
- // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
- // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
- // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
- // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
- // DAMAGES, FOR ANY REASON WHATSOEVER.
- //
- // This is part of revision 6288 of the Stellaris Graphics Library.
- //
- //*****************************************************************************
- #include "debug.h"
- #include "grlib.h"
- //*****************************************************************************
- //
- //! \addtogroup primitives_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, int lX1, int lX2, int lY)
- {
- int 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, int lX, int lY1, int lY2)
- {
- int 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 int
- GrClipCodeGet(const tContext *pContext, int lX, int lY)
- {
- int 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 int
- GrLineClip(const tContext *pContext, int *plX1, int *plY1, int *plX2,
- int *plY2)
- {
- int 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, int lX1, int lY1, int lX2, int lY2)
- {
- int 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.
- //! @}
- //
- //*****************************************************************************
|