From 788db64b8642bf31de6930d18a62177c64163ee0 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Fri, 13 Mar 2015 12:24:02 +0200 Subject: Add grlib, fixes #5 --- grlib/line.c | 639 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 639 insertions(+) create mode 100644 grlib/line.c (limited to 'grlib/line.c') diff --git a/grlib/line.c b/grlib/line.c new file mode 100644 index 0000000..225fd96 --- /dev/null +++ b/grlib/line.c @@ -0,0 +1,639 @@ +//***************************************************************************** +// +// line.c - Routines for drawing lines. +// +// Copyright (c) 2007-2014 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 2.1.0.12573 of the Tiva Graphics Library. +// +//***************************************************************************** + +#include +#include +#include "driverlib/debug.h" +#include "grlib/grlib.h" + +//***************************************************************************** +// +//! \addtogroup primitives_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! Draws a horizontal line. +//! +//! \param pContext is a pointer to the drawing context to use. +//! \param i32X1 is the X coordinate of one end of the line. +//! \param i32X2 is the X coordinate of the other end of the line. +//! \param i32Y 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, int32_t i32X1, int32_t i32X2, + int32_t i32Y) +{ + int32_t i32Temp; + + // + // 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((i32Y < pContext->sClipRegion.i16YMin) || + (i32Y > pContext->sClipRegion.i16YMax)) + { + return; + } + + // + // Swap the X coordinates if the first is larger than the second. + // + if(i32X1 > i32X2) + { + i32Temp = i32X1; + i32X1 = i32X2; + i32X2 = i32Temp; + } + + // + // If the entire line is outside the clipping region, then there is nothing + // to be done. + // + if((i32X1 > pContext->sClipRegion.i16XMax) || + (i32X2 < pContext->sClipRegion.i16XMin)) + { + return; + } + + // + // Clip the starting coordinate to the left side of the clipping region if + // required. + // + if(i32X1 < pContext->sClipRegion.i16XMin) + { + i32X1 = pContext->sClipRegion.i16XMin; + } + + // + // Clip the ending coordinate to the right side of the clipping region if + // required. + // + if(i32X2 > pContext->sClipRegion.i16XMax) + { + i32X2 = pContext->sClipRegion.i16XMax; + } + + // + // Call the low level horizontal line drawing routine. + // + DpyLineDrawH(pContext->psDisplay, i32X1, i32X2, i32Y, + pContext->ui32Foreground); +} + +//***************************************************************************** +// +//! Draws a vertical line. +//! +//! \param pContext is a pointer to the drawing context to use. +//! \param i32X is the X coordinate of the line. +//! \param i32Y1 is the Y coordinate of one end of the line. +//! \param i32Y2 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, int32_t i32X, int32_t i32Y1, + int32_t i32Y2) +{ + int32_t i32Temp; + + // + // 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((i32X < pContext->sClipRegion.i16XMin) || + (i32X > pContext->sClipRegion.i16XMax)) + { + return; + } + + // + // Swap the Y coordinates if the first is larger than the second. + // + if(i32Y1 > i32Y2) + { + i32Temp = i32Y1; + i32Y1 = i32Y2; + i32Y2 = i32Temp; + } + + // + // If the entire line is out of the clipping region, then there is nothing + // to be done. + // + if((i32Y1 > pContext->sClipRegion.i16YMax) || + (i32Y2 < pContext->sClipRegion.i16YMin)) + { + return; + } + + // + // Clip the starting coordinate to the top side of the clipping region if + // required. + // + if(i32Y1 < pContext->sClipRegion.i16YMin) + { + i32Y1 = pContext->sClipRegion.i16YMin; + } + + // + // Clip the ending coordinate to the bottom side of the clipping region if + // required. + // + if(i32Y2 > pContext->sClipRegion.i16YMax) + { + i32Y2 = pContext->sClipRegion.i16YMax; + } + + // + // Call the low level vertical line drawing routine. + // + DpyLineDrawV(pContext->psDisplay, i32X, i32Y1, i32Y2, + pContext->ui32Foreground); +} + +//***************************************************************************** +// +//! Computes the clipping code used by the Cohen-Sutherland clipping algorithm. +//! +//! \param pContext is a pointer to the drawing context to use. +//! \param i32X is the X coordinate of the point. +//! \param i32Y 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 int32_t +GrClipCodeGet(const tContext *pContext, int32_t i32X, int32_t i32Y) +{ + int32_t i32Code; + + // + // Initialize the clipping code to zero. + // + i32Code = 0; + + // + // Set bit zero of the clipping code if the Y coordinate is above the + // clipping region. + // + if(i32Y < pContext->sClipRegion.i16YMin) + { + i32Code |= 1; + } + + // + // Set bit one of the clipping code if the Y coordinate is below the + // clipping region. + // + if(i32Y > pContext->sClipRegion.i16YMax) + { + i32Code |= 2; + } + + // + // Set bit two of the clipping code if the X coordinate is to the left of + // the clipping region. + // + if(i32X < pContext->sClipRegion.i16XMin) + { + i32Code |= 4; + } + + // + // Set bit three of the clipping code if the X coordinate is to the right + // of the clipping region. + // + if(i32X > pContext->sClipRegion.i16XMax) + { + i32Code |= 8; + } + + // + // Return the clipping code. + // + return(i32Code); +} + +//***************************************************************************** +// +//! Clips a line to the clipping region. +//! +//! \param pContext is a pointer to the drawing context to use. +//! \param pi32X1 is the X coordinate of the start of the line. +//! \param pi32Y1 is the Y coordinate of the start of the line. +//! \param pi32X2 is the X coordinate of the end of the line. +//! \param pi32Y2 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 int32_t +GrLineClip(const tContext *pContext, int32_t *pi32X1, int32_t *pi32Y1, + int32_t *pi32X2, int32_t *pi32Y2) +{ + int32_t i32Code, i32Code1, i32Code2, i32X, i32Y; + + // + // Compute the clipping codes for the two endpoints of the line. + // + i32Code1 = GrClipCodeGet(pContext, *pi32X1, *pi32Y1); + i32Code2 = GrClipCodeGet(pContext, *pi32X2, *pi32Y2); + + // + // 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((i32Code1 == 0) && (i32Code2 == 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((i32Code1 & i32Code2) != 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(i32Code1) + { + i32Code = i32Code1; + } + else + { + i32Code = i32Code2; + } + + // + // See if this end of the line lies above the clipping region. + // + if(i32Code & 1) + { + // + // Move this end of the line to the intersection of the line and + // the top of the clipping region. + // + i32X = (*pi32X1 + (((*pi32X2 - *pi32X1) * + (pContext->sClipRegion.i16YMin - *pi32Y1)) / + (*pi32Y2 - *pi32Y1))); + i32Y = pContext->sClipRegion.i16YMin; + } + + // + // Otherwise, see if this end of the line lies below the clipping + // region. + // + else if(i32Code & 2) + { + // + // Move this end of the line to the intersection of the line and + // the bottom of the clipping region. + // + i32X = (*pi32X1 + (((*pi32X2 - *pi32X1) * + (pContext->sClipRegion.i16YMax - *pi32Y1)) / + (*pi32Y2 - *pi32Y1))); + i32Y = pContext->sClipRegion.i16YMax; + } + + // + // Otherwise, see if this end of the line lies to the left of the + // clipping region. + // + else if(i32Code & 4) + { + // + // Move this end of the line to the intersection of the line and + // the left side of the clipping region. + // + i32X = pContext->sClipRegion.i16XMin; + i32Y = (*pi32Y1 + (((*pi32Y2 - *pi32Y1) * + (pContext->sClipRegion.i16XMin - *pi32X1)) / + (*pi32X2 - *pi32X1))); + } + + // + // 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. + // + i32X = pContext->sClipRegion.i16XMax; + i32Y = (*pi32Y1 + (((*pi32Y2 - *pi32Y1) * + (pContext->sClipRegion.i16XMax - *pi32X1)) / + (*pi32X2 - *pi32X1))); + } + + // + // See which end of the line just moved. + // + if(i32Code1) + { + // + // Save the new coordinates for the start of the line. + // + *pi32X1 = i32X; + *pi32Y1 = i32Y; + + // + // Recompute the clipping code for the start of the line. + // + i32Code1 = GrClipCodeGet(pContext, i32X, i32Y); + } + else + { + // + // Save the new coordinates for the end of the line. + // + *pi32X2 = i32X; + *pi32Y2 = i32Y; + + // + // Recompute the clipping code for the end of the line. + // + i32Code2 = GrClipCodeGet(pContext, i32X, i32Y); + } + } +} + +//***************************************************************************** +// +//! Draws a line. +//! +//! \param pContext is a pointer to the drawing context to use. +//! \param i32X1 is the X coordinate of the start of the line. +//! \param i32Y1 is the Y coordinate of the start of the line. +//! \param i32X2 is the X coordinate of the end of the line. +//! \param i32Y2 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, int32_t i32X1, int32_t i32Y1, + int32_t i32X2, int32_t i32Y2) +{ + int32_t i32Error, i32DeltaX, i32DeltaY, i32YStep, bSteep; + + // + // Check the arguments. + // + ASSERT(pContext); + + // + // See if this is a vertical line. + // + if(i32X1 == i32X2) + { + // + // 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, i32X1, i32Y1, i32Y2); + + // + // The line has ben drawn, so return. + // + return; + } + + // + // See if this is a horizontal line. + // + if(i32Y1 == i32Y2) + { + // + // 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, i32X1, i32X2, i32Y1); + + // + // 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, &i32X1, &i32Y1, &i32X2, &i32Y2) == 0) + { + return; + } + + // + // Determine if the line is steep. A steep line has more motion in the Y + // direction than the X direction. + // + if(((i32Y2 > i32Y1) ? (i32Y2 - i32Y1) : (i32Y1 - i32Y2)) > + ((i32X2 > i32X1) ? (i32X2 - i32X1) : (i32X1 - i32X2))) + { + bSteep = 1; + } + else + { + bSteep = 0; + } + + // + // If the line is steep, then swap the X and Y coordinates. + // + if(bSteep) + { + i32Error = i32X1; + i32X1 = i32Y1; + i32Y1 = i32Error; + i32Error = i32X2; + i32X2 = i32Y2; + i32Y2 = i32Error; + } + + // + // If the starting X coordinate is larger than the ending X coordinate, + // then swap the start and end coordinates. + // + if(i32X1 > i32X2) + { + i32Error = i32X1; + i32X1 = i32X2; + i32X2 = i32Error; + i32Error = i32Y1; + i32Y1 = i32Y2; + i32Y2 = i32Error; + } + + // + // Compute the difference between the start and end coordinates in each + // axis. + // + i32DeltaX = i32X2 - i32X1; + i32DeltaY = (i32Y2 > i32Y1) ? (i32Y2 - i32Y1) : (i32Y1 - i32Y2); + + // + // Initialize the error term to negative half the X delta. + // + i32Error = -i32DeltaX / 2; + + // + // Determine the direction to step in the Y axis when required. + // + if(i32Y1 < i32Y2) + { + i32YStep = 1; + } + else + { + i32YStep = -1; + } + + // + // Loop through all the points along the X axis of the line. + // + for(; i32X1 <= i32X2; i32X1++) + { + // + // See if this is a steep line. + // + if(bSteep) + { + // + // Plot this point of the line, swapping the X and Y coordinates. + // + DpyPixelDraw(pContext->psDisplay, i32Y1, i32X1, + pContext->ui32Foreground); + } + else + { + // + // Plot this point of the line, using the coordinates as is. + // + DpyPixelDraw(pContext->psDisplay, i32X1, i32Y1, + pContext->ui32Foreground); + } + + // + // Increment the error term by the Y delta. + // + i32Error += i32DeltaY; + + // + // See if the error term is now greater than zero. + // + if(i32Error > 0) + { + // + // Take a step in the Y axis. + // + i32Y1 += i32YStep; + + // + // Decrement the error term by the X delta. + // + i32Error -= i32DeltaX; + } + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** -- cgit v1.3.1