/* --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. //! @} // //*****************************************************************************