windows-nt/Source/XPSP1/NT/drivers/video/ms/cirrus/disp/lineto.c
2020-09-26 16:20:57 +08:00

880 lines
28 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/******************************************************************************\
*
* $Workfile: LineTo.c $
*
* Contents:
* This file contains the DrvLineTo function and simple line drawing code.
*
* Copyright (c) 1996 Cirrus Logic, Inc.
*
* $Log: V:/CirrusLogic/CL54xx/NT40/Archive/Display/LineTo.c_v $
*
* Rev 1.4 12 Aug 1996 16:53:50 frido
* Added NT 3.5x/4.0 auto detection.
*
* Rev 1.3 29 Jul 1996 12:23:04 frido
* Fixed bug in drawing horizontal lines from right to left.
*
* Rev 1.2 15 Jul 1996 15:56:12 frido
* Changed DST_ADDR into DST_ADDR_ABS.
*
* Rev 1.1 12 Jul 1996 16:02:06 frido
* Redefined some macros that caused irratic line drawing on device bitmaps.
*
* Rev 1.0 10 Jul 1996 17:53:40 frido
* New code.
*
\******************************************************************************/
#include "PreComp.h"
#if LINETO
#define LEFT 0x01
#define TOP 0x02
#define RIGHT 0x04
#define BOTTOM 0x08
bIoLineTo(
PDEV* ppdev,
LONG x1,
LONG y1,
LONG x2,
LONG y2,
ULONG ulSolidColor,
MIX mix,
ULONG ulDstAddr)
{
BYTE* pjPorts = ppdev->pjPorts;
LONG lDelta = ppdev->lDelta;
LONG dx, dy;
LONG cx, cy;
if (ulSolidColor != (ULONG) -1)
{
if (ppdev->cBpp == 1)
{
ulSolidColor |= ulSolidColor << 8;
ulSolidColor |= ulSolidColor << 16;
}
else if (ppdev->cBpp == 2)
{
ulSolidColor |= ulSolidColor << 16;
}
CP_IO_WAIT_FOR_BLT_COMPLETE(ppdev, pjPorts);
CP_IO_ROP(ppdev, pjPorts, gajHwMixFromMix[mix & 0x0F]);
CP_IO_SRC_ADDR(ppdev, pjPorts, ppdev->ulSolidColorOffset);
CP_IO_BLT_MODE(ppdev, pjPorts, ENABLE_COLOR_EXPAND |
ENABLE_8x8_PATTERN_COPY |
ppdev->jModeColor);
CP_IO_FG_COLOR(ppdev, pjPorts, ulSolidColor);
}
// Calculate deltas.
dx = x2 - x1;
dy = y2 - y1;
// Horizontal lines.
if (dy == 0)
{
if (dx < 0)
{
// From right to left.
ulDstAddr += PELS_TO_BYTES(x2 - 1) + (y2 * lDelta);
cx = PELS_TO_BYTES(-dx) - 1;
}
else if (dx > 0)
{
// From left to right.
ulDstAddr += PELS_TO_BYTES(x1) + (y1 * lDelta);
cx = PELS_TO_BYTES(dx) - 1;
}
else
{
// Nothing to do here!
return(TRUE);
}
CP_IO_WAIT_FOR_BLT_COMPLETE(ppdev, pjPorts);
CP_IO_XCNT(ppdev, pjPorts, cx);
CP_IO_YCNT(ppdev, pjPorts, 0);
CP_IO_DST_ADDR_ABS(ppdev, pjPorts, ulDstAddr);
CP_IO_START_BLT(ppdev, pjPorts);
return(TRUE);
}
// Vertical lines.
else if (dx == 0)
{
if (dy < 0)
{
// From bottom to top.
ulDstAddr += PELS_TO_BYTES(x2) + ((y2 + 1) * lDelta);
cy = -dy - 1;
}
else
{
// From top to bottom.
ulDstAddr += PELS_TO_BYTES(x1) + (y1 * lDelta);
cy = dy - 1;
}
cx = PELS_TO_BYTES(1) - 1;
CP_IO_WAIT_FOR_BLT_COMPLETE(ppdev, pjPorts);
CP_IO_XCNT(ppdev, pjPorts, cx);
CP_IO_YCNT(ppdev, pjPorts, cy);
CP_IO_DST_Y_OFFSET(ppdev, pjPorts, lDelta);
CP_IO_DST_ADDR_ABS(ppdev, pjPorts, ulDstAddr);
CP_IO_START_BLT(ppdev, pjPorts);
return(TRUE);
}
// Diagonal lines.
else if ((dx == dy) || (dx == -dy))
{
if (dy < 0)
{
if (dx < 0)
{
// Diagonal line from bottom-right to upper-left.
ulDstAddr += PELS_TO_BYTES(x2 + 1);
}
else
{
// Diagonal line from bottom-left to upper-right.
ulDstAddr += PELS_TO_BYTES(x2 - 1);
}
ulDstAddr += (y2 + 1) * lDelta;
cy = -dy - 1;
}
else
{
// Diagonal line from top to bottom, either from left to right or
// right to left.
ulDstAddr += PELS_TO_BYTES(x1) + (y1 * lDelta);
cy = dy - 1;
}
if (dx == dy)
{
// Diagonal line from top-left to bottom-right or vice versa.
lDelta += PELS_TO_BYTES(1);
}
else
{
// Diagonal line from top-right to bottom-left or vice versa.
lDelta -= PELS_TO_BYTES(1);
}
cx = PELS_TO_BYTES(1) - 1;
CP_IO_WAIT_FOR_BLT_COMPLETE(ppdev, pjPorts);
CP_IO_XCNT(ppdev, pjPorts, cx);
CP_IO_YCNT(ppdev, pjPorts, cy);
CP_IO_DST_Y_OFFSET(ppdev, pjPorts, lDelta);
CP_IO_DST_ADDR_ABS(ppdev, pjPorts, ulDstAddr);
CP_IO_START_BLT(ppdev, pjPorts);
return(TRUE);
}
// All other lines.
if (dx < 0)
{
dx = -dx;
}
if (dy < 0)
{
dy = -dy;
}
ulDstAddr += PELS_TO_BYTES(x1) + (y1 * lDelta);
// Horizontal major.
if (dx > dy)
{
LONG run = dy;
cy = (y1 > y2) ? -lDelta : lDelta;
CP_IO_WAIT_FOR_BLT_COMPLETE(ppdev, pjPorts);
//
// We would like to set the YCNT register once
// here (outside the loops below). However, on
// the CL5428, this register does not hold its value
// after one iteration through the loop. So, I'll
// have to set it inside the loop.
//
if (x1 < x2)
{
while (x1 < x2)
{
cx = 1 + (dx - run) / dy;
if ((x1 + cx) < x2)
{
run += cx * dy - dx;
}
else
{
cx = x2 - x1;
}
x1 += cx;
cx = PELS_TO_BYTES(cx);
CP_IO_WAIT_FOR_BLT_COMPLETE(ppdev, pjPorts);
CP_IO_YCNT(ppdev, pjPorts, 0);
CP_IO_XCNT(ppdev, pjPorts, cx - 1);
CP_IO_DST_ADDR_ABS(ppdev, pjPorts, ulDstAddr);
CP_IO_START_BLT(ppdev, pjPorts);
ulDstAddr += cx + cy;
}
}
else
{
cy -= PELS_TO_BYTES(1);
while (x1 > x2)
{
cx = 1 + (dx - run) / dy;
if ((x1 - cx) > x2)
{
run += cx * dy - dx;
}
else
{
cx = x1 - x2;
}
ulDstAddr -= PELS_TO_BYTES(cx - 1);
x1 -= cx;
cx = PELS_TO_BYTES(cx);
CP_IO_WAIT_FOR_BLT_COMPLETE(ppdev, pjPorts);
CP_IO_YCNT(ppdev, pjPorts, 0);
CP_IO_XCNT(ppdev, pjPorts, cx - 1);
CP_IO_DST_ADDR_ABS(ppdev, pjPorts, ulDstAddr);
CP_IO_START_BLT(ppdev, pjPorts);
ulDstAddr += cy;
}
}
}
// Vertical major.
else
{
LONG run = dx;
cx = (x1 > x2) ? PELS_TO_BYTES(-1) : PELS_TO_BYTES(1);
CP_IO_WAIT_FOR_BLT_COMPLETE(ppdev, pjPorts);
CP_IO_XCNT(ppdev, pjPorts, PELS_TO_BYTES(1) - 1);
CP_IO_DST_Y_OFFSET(ppdev, pjPorts, lDelta);
if (y1 < y2)
{
while (y1 < y2)
{
cy = 1 + (dy - run) / dx;
if ((y1 + cy) < y2)
{
run += cy * dx - dy;
}
else
{
cy = y2 - y1;
}
y1 += cy;
CP_IO_WAIT_FOR_BLT_COMPLETE(ppdev, pjPorts);
CP_IO_YCNT(ppdev, pjPorts, cy - 1);
CP_IO_DST_ADDR_ABS(ppdev, pjPorts, ulDstAddr);
CP_IO_START_BLT(ppdev, pjPorts);
ulDstAddr += cx + cy * lDelta;
}
}
else
{
cx -= lDelta;
while (y1 > y2)
{
cy = 1 + (dy - run) / dx;
if ((y1 - cy) > y2)
{
run += cy * dx - dy;
}
else
{
cy = y1 - y2;
}
ulDstAddr -= (cy - 1) * lDelta;
y1 -= cy;
CP_IO_WAIT_FOR_BLT_COMPLETE(ppdev, pjPorts);
CP_IO_YCNT(ppdev, pjPorts, cy - 1);
CP_IO_DST_ADDR_ABS(ppdev, pjPorts, ulDstAddr);
CP_IO_START_BLT(ppdev, pjPorts);
ulDstAddr += cx;
}
}
}
return(TRUE);
}
bMmLineTo(
PDEV* ppdev,
LONG x1,
LONG y1,
LONG x2,
LONG y2,
ULONG ulSolidColor,
MIX mix,
ULONG ulDstAddr)
{
BYTE* pjBase = ppdev->pjBase;
LONG lDelta = ppdev->lDelta;
LONG dx, dy;
LONG cx, cy;
if (ulSolidColor != (ULONG) -1)
{
if (ppdev->cBpp == 1)
{
ulSolidColor |= ulSolidColor << 8;
ulSolidColor |= ulSolidColor << 16;
}
else if (ppdev->cBpp == 2)
{
ulSolidColor |= ulSolidColor << 16;
}
CP_MM_WAIT_FOR_BLT_COMPLETE(ppdev, pjBase);
CP_MM_ROP(ppdev, pjBase, gajHwMixFromMix[mix & 0x0F]);
CP_MM_SRC_ADDR(ppdev, pjBase, ppdev->ulSolidColorOffset);
CP_MM_BLT_MODE(ppdev, pjBase, ENABLE_COLOR_EXPAND |
ENABLE_8x8_PATTERN_COPY |
ppdev->jModeColor);
CP_MM_FG_COLOR(ppdev, pjBase, ulSolidColor);
// if (ppdev->flCaps & CAPS_IS_5436)
if (ppdev->flCaps & CAPS_AUTOSTART)
{
CP_MM_BLT_EXT_MODE(ppdev, pjBase, ENABLE_SOLID_FILL);
}
}
// Calculate deltas.
dx = x2 - x1;
dy = y2 - y1;
// Horizontal lines.
if (dy == 0)
{
if (dx < 0)
{
// From right to left.
ulDstAddr += PELS_TO_BYTES(x2 + 1) + (y2 * lDelta);
cx = PELS_TO_BYTES(-dx) - 1;
}
else if (dx > 0)
{
// From left to right.
ulDstAddr += PELS_TO_BYTES(x1) + (y1 * lDelta);
cx = PELS_TO_BYTES(dx) - 1;
}
else
{
// Nothing to do here!
return(TRUE);
}
CP_MM_WAIT_FOR_BLT_COMPLETE(ppdev, pjBase);
CP_MM_XCNT(ppdev, pjBase, cx);
CP_MM_YCNT(ppdev, pjBase, 0);
CP_MM_DST_ADDR_ABS(ppdev, pjBase, ulDstAddr);
CP_MM_START_BLT(ppdev, pjBase);
return(TRUE);
}
// Vertical lines.
else if (dx == 0)
{
if (dy < 0)
{
// From bottom to top.
ulDstAddr += PELS_TO_BYTES(x2) + ((y2 + 1) * lDelta);
cy = -dy - 1;
}
else
{
// From top to bottom.
ulDstAddr += PELS_TO_BYTES(x1) + (y1 * lDelta);
cy = dy - 1;
}
cx = PELS_TO_BYTES(1) - 1;
CP_MM_WAIT_FOR_BLT_COMPLETE(ppdev, pjBase);
CP_MM_XCNT(ppdev, pjBase, cx);
CP_MM_YCNT(ppdev, pjBase, cy);
CP_MM_DST_Y_OFFSET(ppdev, pjBase, lDelta);
CP_MM_DST_ADDR_ABS(ppdev, pjBase, ulDstAddr);
CP_MM_START_BLT(ppdev, pjBase);
return(TRUE);
}
// Diagonal lines.
else if ((dx == dy) || (dx == -dy))
{
if (dy < 0)
{
if (dx < 0)
{
// Diagonal line from bottom-right to upper-left.
ulDstAddr += PELS_TO_BYTES(x2 + 1);
}
else
{
// Diagonal line from bottom-left to upper-right.
ulDstAddr += PELS_TO_BYTES(x2 - 1);
}
ulDstAddr += (y2 + 1) * lDelta;
cy = -dy - 1;
}
else
{
// Diagonal line from top to bottom, either from left to right or
// right to left.
ulDstAddr += PELS_TO_BYTES(x1) + (y1 * lDelta);
cy = dy - 1;
}
if (dx == dy)
{
// Diagonal line from top-left to bottom-right or vice versa.
lDelta += PELS_TO_BYTES(1);
}
else
{
// Diagonal line from top-right to bottom-left or vice versa.
lDelta -= PELS_TO_BYTES(1);
}
cx = PELS_TO_BYTES(1) - 1;
CP_MM_WAIT_FOR_BLT_COMPLETE(ppdev, pjBase);
CP_MM_XCNT(ppdev, pjBase, cx);
CP_MM_YCNT(ppdev, pjBase, cy);
CP_MM_DST_Y_OFFSET(ppdev, pjBase, lDelta);
CP_MM_DST_ADDR_ABS(ppdev, pjBase, ulDstAddr);
CP_MM_START_BLT(ppdev, pjBase);
return(TRUE);
}
// All other lines.
if (dx < 0)
{
dx = -dx;
}
if (dy < 0)
{
dy = -dy;
}
ulDstAddr += PELS_TO_BYTES(x1) + (y1 * lDelta);
// Horizontal major.
if (dx > dy)
{
LONG run = dy;
cy = (y1 > y2) ? -lDelta : lDelta;
CP_MM_WAIT_FOR_BLT_COMPLETE(ppdev, pjBase);
CP_MM_YCNT(ppdev, pjBase, 0);
if (x1 < x2)
{
while (x1 < x2)
{
cx = 1 + (dx - run) / dy;
if ((x1 + cx) < x2)
{
run += cx * dy - dx;
}
else
{
cx = x2 - x1;
}
x1 += cx;
cx = PELS_TO_BYTES(cx);
CP_MM_WAIT_FOR_BLT_COMPLETE(ppdev, pjBase);
CP_MM_XCNT(ppdev, pjBase, cx - 1);
CP_MM_DST_ADDR_ABS(ppdev, pjBase, ulDstAddr);
CP_MM_START_BLT(ppdev, pjBase);
ulDstAddr += cx + cy;
}
}
else
{
cy -= PELS_TO_BYTES(1);
while (x1 > x2)
{
cx = 1 + (dx - run) / dy;
if ((x1 - cx) > x2)
{
run += cx * dy - dx;
}
else
{
cx = x1 - x2;
}
ulDstAddr -= PELS_TO_BYTES(cx - 1);
x1 -= cx;
cx = PELS_TO_BYTES(cx);
CP_MM_WAIT_FOR_BLT_COMPLETE(ppdev, pjBase);
CP_MM_XCNT(ppdev, pjBase, cx - 1);
CP_MM_DST_ADDR_ABS(ppdev, pjBase, ulDstAddr);
CP_MM_START_BLT(ppdev, pjBase);
ulDstAddr += cy;
}
}
}
// Vertical major.
else
{
LONG run = dx;
cx = (x1 > x2) ? PELS_TO_BYTES(-1) : PELS_TO_BYTES(1);
CP_MM_WAIT_FOR_BLT_COMPLETE(ppdev, pjBase);
CP_MM_XCNT(ppdev, pjBase, PELS_TO_BYTES(1) - 1);
CP_MM_DST_Y_OFFSET(ppdev, pjBase, lDelta);
if (y1 < y2)
{
while (y1 < y2)
{
cy = 1 + (dy - run) / dx;
if ((y1 + cy) < y2)
{
run += cy * dx - dy;
}
else
{
cy = y2 - y1;
}
y1 += cy;
CP_MM_WAIT_FOR_BLT_COMPLETE(ppdev, pjBase);
CP_MM_YCNT(ppdev, pjBase, cy - 1);
CP_MM_DST_ADDR_ABS(ppdev, pjBase, ulDstAddr);
CP_MM_START_BLT(ppdev, pjBase);
ulDstAddr += cx + cy * lDelta;
}
}
else
{
cx -= lDelta;
while (y1 > y2)
{
cy = 1 + (dy - run) / dx;
if ((y1 - cy) > y2)
{
run += cy * dx - dy;
}
else
{
cy = y1 - y2;
}
ulDstAddr -= (cy - 1) * lDelta;
y1 -= cy;
CP_MM_WAIT_FOR_BLT_COMPLETE(ppdev, pjBase);
CP_MM_YCNT(ppdev, pjBase, cy - 1);
CP_MM_DST_ADDR_ABS(ppdev, pjBase, ulDstAddr);
CP_MM_START_BLT(ppdev, pjBase);
ulDstAddr += cx;
}
}
}
return(TRUE);
}
BOOL bClipLine(LONG x1, LONG y1, LONG x2, LONG y2, RECTL* prcl)
{
ULONG ulCode1, ulCode2;
RECTL rclClip1, rclClip2;
LONG dx, dy;
// Set clipping rectangles.
rclClip1.left = prcl->left;
rclClip1.top = prcl->top;
rclClip1.right = prcl->right - 1;
rclClip1.bottom = prcl->bottom - 1;
rclClip2.left = prcl->left - 1;
rclClip2.top = prcl->top - 1;
rclClip2.right = prcl->right;
rclClip2.bottom = prcl->bottom;
// Set line deltas.
dx = x2 - x1;
dy = y2 - y1;
// Set line flags.
ulCode1 = 0;
if (x1 < rclClip1.left) ulCode1 |= LEFT;
if (y1 < rclClip1.top) ulCode1 |= TOP;
if (x1 > rclClip1.right) ulCode1 |= RIGHT;
if (y1 > rclClip1.bottom) ulCode1 |= BOTTOM;
ulCode2 = 0;
if (x2 < rclClip2.left) ulCode2 |= LEFT;
if (y2 < rclClip2.top) ulCode2 |= TOP;
if (x2 > rclClip2.right) ulCode2 |= RIGHT;
if (y2 > rclClip2.bottom) ulCode2 |= BOTTOM;
if ((ulCode1 & ulCode2) != 0)
{
// The line is completly clipped.
return(FALSE);
}
// Vertical lines.
if (dx == 0)
{
if (dy == 0)
{
return(FALSE);
}
if (ulCode1 & TOP)
{
y1 = rclClip1.top;
}
else if (ulCode1 & BOTTOM)
{
y1 = rclClip1.bottom;
}
if (ulCode2 & TOP)
{
y2 = rclClip2.top;
}
else if (ulCode2 & BOTTOM)
{
y2 = rclClip2.bottom;
}
goto ReturnTrue;
}
// Horizontal lines.
if (dy == 0)
{
if (ulCode1 & LEFT)
{
x1 = rclClip1.left;
}
else if (ulCode1 & RIGHT)
{
x1 = rclClip1.right;
}
if (ulCode2 & LEFT)
{
x2 = rclClip2.left;
}
else if (ulCode2 & RIGHT)
{
x2 = rclClip2.right;
}
goto ReturnTrue;
}
// Clip start point.
if (x1 < rclClip1.left)
{
y1 += dy * (rclClip1.left - x1) / dx;
x1 = rclClip1.left;
}
else if (x1 > rclClip1.right)
{
y1 += dy * (rclClip1.right - x1) / dx;
x1 = rclClip1.right;
}
if (y1 < rclClip1.top)
{
x1 += dx * (rclClip1.top - y1) / dy;
y1 = rclClip1.top;
}
else if (y1 > rclClip1.bottom)
{
x1 += dx * (rclClip1.bottom - y1) / dy;
y1 = rclClip1.bottom;
}
if ((x1 < rclClip1.left) || (y1 < rclClip1.top) || (x1 > rclClip1.right) ||
(y1 > rclClip1.bottom))
{
// Start point fully clipped.
return(FALSE);
}
// Clip end point.
if (x2 < rclClip2.left)
{
y2 += dy * (rclClip2.left - x2) / dx;
x2 = rclClip2.left;
}
else if (x2 > rclClip2.right)
{
y2 += dy * (rclClip2.right - x2) / dx;
x2 = rclClip2.right;
}
if (y2 < rclClip2.top)
{
x2 += dx * (rclClip2.top - y2) / dy;
y2 = rclClip2.top;
}
else if (y2 > rclClip2.bottom)
{
x2 += dx * (rclClip2.bottom - y2) / dy;
y2 = rclClip2.bottom;
}
if ((x2 < rclClip2.left) || (y2 < rclClip2.top) || (x2 > rclClip2.right) ||
(y2 > rclClip2.bottom))
{
// End point fully clipped.
return(FALSE);
}
ReturnTrue:
prcl->left = x1;
prcl->top = y1;
prcl->right = x2;
prcl->bottom = y2;
return(TRUE);
}
/******************************************************************************\
*
* Function: DrvLineTo
*
* This function draws a line between any two points. This function only draws
* lines in solod color and that are just 1 pixel wide. The end-point is not
* drawn.
*
* Parameters: pso Pointer to surface.
* pco Pointer to CLIPOBJ.
* pbo Pointer to BRUSHOBJ.
* x1 Starting x-coordinate.
* y1 Starting y-coordinate.
* x2 Ending x-coordinate.
* y2 Ending y-coordinate.
* prclBounds Pointer to an unclipped bounding rectangle.
* mix Mix to perform on the destination.
*
* Returns: TRUE if the line has been drawn, FALSE oftherwise.
*
\******************************************************************************/
BOOL DrvLineTo(
SURFOBJ* pso,
CLIPOBJ* pco,
BRUSHOBJ* pbo,
LONG x1,
LONG y1,
LONG x2,
LONG y2,
RECTL* prclBounds,
MIX mix)
{
PDEV* ppdev = (PPDEV)pso->dhpdev;
DSURF* pdsurf = (DSURF *)pso->dhsurf;
OH* poh;
BOOL bMore;
// If the device bitmap is a DIB, let GDI handle it.
if (pdsurf->dt == DT_DIB)
{
return(EngLineTo(pdsurf->pso, pco, pbo, x1, y1, x2, y2, prclBounds,
mix));
}
// Get the off-screen node.
poh = pdsurf->poh;
if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
{
// No clipping.
return(ppdev->pfnLineTo(ppdev, x1, y1, x2, y2, pbo->iSolidColor, mix,
poh->xy));
}
else if (pco->iDComplexity == DC_RECT)
{
// Clipped rectangle.
RECTL rcl;
rcl = pco->rclBounds;
if (bClipLine(x1, y1, x2, y2, &rcl))
{
return(ppdev->pfnLineTo(ppdev, rcl.left, rcl.top, rcl.right,
rcl.bottom, pbo->iSolidColor, mix,
poh->xy));
}
return(TRUE);
}
// Complex clipping.
CLIPOBJ_cEnumStart(pco, FALSE, CT_RECTANGLES, CD_ANY, 0);
do
{
CLIPENUM ce;
RECTL* prcl;
bMore = CLIPOBJ_bEnum(pco, sizeof(ce), &ce.c);
prcl = ce.arcl;
while (ce.c--)
{
if (bClipLine(x1, y1, x2, y2, prcl))
{
if (!ppdev->pfnLineTo(ppdev, prcl->left, prcl->top, prcl->right,
prcl->bottom, pbo->iSolidColor, mix,
poh->xy))
{
return(FALSE);
}
}
prcl++;
}
} while (bMore);
return(TRUE);
}
#endif // LINETO