534 lines
16 KiB
C
534 lines
16 KiB
C
/* SDL_prims.c -- 2D graphical primitives for SDL
|
|
*
|
|
* Copyright (c) 2008 Ian Piumarta
|
|
* All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the 'Software'),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, provided that the above copyright notice(s) and this
|
|
* permission notice appear in all copies of the Software and that both the
|
|
* above copyright notice(s) and this permission notice appear in supporting
|
|
* documentation.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED 'AS IS'. USE ENTIRELY AT YOUR OWN RISK.
|
|
*
|
|
* Last edited: 2008-06-12 12:32:57 by piumarta on WINDOWS-XP.piumarta.com
|
|
*/
|
|
|
|
#include <malloc.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include "SDL_prims.h"
|
|
|
|
#define abs(N) (((N) < 0) ? -(N) : (N))
|
|
#define sgn(N) (((N) < 0) ? -1 : ((N) > 0 ? 1 : 0))
|
|
#define swap(T, A, B) do { T tmp= A; A= B; B= tmp; } while (0)
|
|
#define min(A, B) (((A) < (B) ? (A) : (B)))
|
|
#define max(A, B) (((A) > (B) ? (A) : (B)))
|
|
#define clamp(A, X, B) min(max(A, X), B)
|
|
|
|
#define CLIPX0(S) ((S)->clip_rect.x)
|
|
#define CLIPY0(S) ((S)->clip_rect.y)
|
|
#define CLIPW(S) ((S)->clip_rect.w)
|
|
#define CLIPH(S) ((S)->clip_rect.h)
|
|
#define CLIPX1(S) (CLIPX0(S) + CLIPW(S))
|
|
#define CLIPY1(S) (CLIPY0(S) + CLIPH(S))
|
|
|
|
#define CLIPX(S, X) clamp(CLIPX0(S), X, CLIPX1(S) - 1)
|
|
#define CLIPY(S, Y) clamp(CLIPY0(S), Y, CLIPY1(S) - 1)
|
|
|
|
#define INCLIPX(S, X) ((CLIPX0(S) <= (X)) && ((X) < CLIPX1(S)))
|
|
#define INCLIPY(S, Y) ((CLIPY0(S) <= (Y)) && ((Y) < CLIPY1(S)))
|
|
#define INCLIP(S, X, Y) (INCLIPX(S, X) && INCLIPY(S, Y))
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
/* DrawPixel */
|
|
/* ---------------------------------------------------------------- */
|
|
|
|
static inline int DrawPixel8(SDL_Surface *s, int x, int y, Uint32 c)
|
|
{
|
|
*((Uint8 *)(s->pixels + s->pitch * y + x))= (Uint8)c;
|
|
return 0;
|
|
}
|
|
|
|
static inline int DrawPixel16(SDL_Surface *s, int x, int y, Uint32 c)
|
|
{
|
|
*((Uint16 *)(s->pixels + s->pitch * y + 2 * x))= (Uint16)c;
|
|
return 0;
|
|
}
|
|
|
|
static inline int DrawPixel32(SDL_Surface *s, int x, int y, Uint32 c)
|
|
{
|
|
*((Uint32 *)(s->pixels + s->pitch * y + 4 * x))= (Uint32)c;
|
|
return 0;
|
|
}
|
|
|
|
int SDL_DrawPixel(SDL_Surface *s, int x, int y, Uint32 c)
|
|
{
|
|
if (INCLIP(s, x, y))
|
|
{
|
|
switch (s->format->BytesPerPixel)
|
|
{
|
|
case 1: return DrawPixel8 (s, x, y, c);
|
|
case 2: return DrawPixel16(s, x, y, c);
|
|
case 3:
|
|
# if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
|
colour <<= 8;
|
|
# endif
|
|
case 4: return DrawPixel32(s, x, y, c);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
|
|
/* Cohen-Sutherland clipping algorithm.
|
|
* James D. Foley, Andries van Dam, et al, "Computer Graphics:
|
|
* Principles and Practice", Addison-Wesley, 1995. ISBN 0201848406.
|
|
* (Section 3.12.3.) */
|
|
|
|
enum {
|
|
ClipLeft = 0x1,
|
|
ClipRight = 0x2,
|
|
ClipBottom = 0x4,
|
|
ClipTop = 0x8
|
|
};
|
|
|
|
#define CLIP_INSIDE(C) (!(C))
|
|
#define CLIP_REJECT(A, B) ((A) & (B))
|
|
#define CLIP_ACCEPT(A, B) (!((A)|(B)))
|
|
|
|
static int clipEncode(int x, int y, int l, int t, int r, int b)
|
|
{
|
|
int code= 0;
|
|
if (x < l) code |= ClipLeft;
|
|
else if (x > r) code |= ClipRight;
|
|
if (y < t) code |= ClipTop;
|
|
else if (y > b) code |= ClipBottom;
|
|
return code;
|
|
}
|
|
|
|
static int clipLine(SDL_Surface *dst, int *x1, int *y1, int *x2, int *y2)
|
|
{
|
|
int visible= 0;
|
|
int l= dst->clip_rect.x;
|
|
int r= dst->clip_rect.x + dst->clip_rect.w - 1;
|
|
int t= dst->clip_rect.y;
|
|
int b= dst->clip_rect.y + dst->clip_rect.h - 1;
|
|
|
|
for (;;)
|
|
{
|
|
int code1= clipEncode(*x1, *y1, l, t, r, b);
|
|
int code2= clipEncode(*x2, *y2, l, t, r, b);
|
|
if (CLIP_ACCEPT(code1, code2))
|
|
{
|
|
visible= 1;
|
|
break;
|
|
}
|
|
else if (CLIP_REJECT(code1, code2))
|
|
break;
|
|
else
|
|
{
|
|
double m;
|
|
if (CLIP_INSIDE(code1))
|
|
{
|
|
swap(int, *x1, *x2);
|
|
swap(int, *y1, *y2);
|
|
swap(int, code1, code2);
|
|
}
|
|
m= ((*x2 != *x1) ? (*y2 - *y1) / (double)(*x2 - *x1) : 1.0L);
|
|
if (code1 & ClipLeft) { *y1 += (int)((l - *x1) * m); *x1= l; }
|
|
else if (code1 & ClipRight) { *y1 += (int)((r - *x1) * m); *x1= r; }
|
|
else if (code1 & ClipBottom) { if (*x2 != *x1) *x1 += (int)((b - *y1) / m); *y1= b; }
|
|
else if (code1 & ClipTop) { if (*x2 != *x1) *x1 += (int)((t - *y1) / m); *y1= t; }
|
|
}
|
|
}
|
|
return visible;
|
|
}
|
|
|
|
/* Dynamic differential analyser for lines.
|
|
* Jack E. Bresenham, "Algorithm for computer control of a digital
|
|
* plotter", IBM Systems Journal 4(1), January 1965, pp. 25--30.
|
|
* (Special-cased for horiz/vert; general case optimised to remove
|
|
* conditionals from inner loop.) */
|
|
|
|
#define DrawLine(N, M) \
|
|
static int DrawHLine##N(SDL_Surface *s, int x1, int y1, int x2, Uint32 colour) \
|
|
{ \
|
|
Uint8 *p= s->pixels + s->pitch * y1; \
|
|
Uint##M c= (Uint##M)colour; \
|
|
while (x1 <= x2) \
|
|
{ \
|
|
*((Uint##M *)(p + ((M)>>3) * x1))= c; \
|
|
++x1; \
|
|
} \
|
|
return 0; \
|
|
} \
|
|
\
|
|
static int DrawVLine##N(SDL_Surface *s, int x1, int y1, int y2, Uint32 colour) \
|
|
{ \
|
|
Uint8 *p= s->pixels + ((M)>>3) * x1; \
|
|
Uint##M c= (Uint##M)colour; \
|
|
while (y1 <= y2) \
|
|
{ \
|
|
*((Uint##M *)(p + s->pitch * y1))= c; \
|
|
++y1; \
|
|
} \
|
|
return 0; \
|
|
} \
|
|
\
|
|
static int DrawLine##N(SDL_Surface *s, int x1, int y1, int x2, int y2, Uint32 colour) \
|
|
{ \
|
|
Uint##M c= (Uint##M)colour; \
|
|
int nx= x2 - x1, ax= abs(nx); \
|
|
int ny= y2 - y1, ay= abs(ny); \
|
|
if (ax >= ay) /* horizontal */ \
|
|
{ \
|
|
int x= x1, dx= sgn(nx); \
|
|
double y= y1, dy= (double)(y2 - y1) / (double)ax; \
|
|
while (ax-- >= 0) \
|
|
{ \
|
|
*((Uint##M *)(s->pixels + s->pitch * (int)(y) + ((M)>>3) * x))= c; \
|
|
x += dx; \
|
|
y += dy; \
|
|
} \
|
|
} \
|
|
else /* vertical */ \
|
|
{ \
|
|
int y= y1, dy= sgn(ny); \
|
|
double x= x1, dx= (double)(x2 - x1) / (double)ay; \
|
|
while (ay-- >= 0) \
|
|
{ \
|
|
*((Uint##M *)(s->pixels + s->pitch * y + ((M)>>3) * (int)(x)))= c; \
|
|
y += dy; \
|
|
x += dx; \
|
|
} \
|
|
} \
|
|
return 0; \
|
|
}
|
|
|
|
DrawLine( 8, 8)
|
|
DrawLine(16, 16)
|
|
DrawLine(32, 32)
|
|
|
|
#undef DrawLine
|
|
|
|
int SDL_DrawHLine(SDL_Surface *s, int x1, int y1, int x2, Uint32 colour)
|
|
{
|
|
if (INCLIPY(s, y1) && (INCLIPX(s, x1) || INCLIPX(s, x2)))
|
|
{
|
|
if (x1 > x2) swap(int, x1, x2);
|
|
x1= CLIPX(s, x1);
|
|
x2= CLIPX(s, x2);
|
|
switch (s->format->BytesPerPixel)
|
|
{
|
|
case 1: return DrawHLine8 (s, x1, y1, x2, colour);
|
|
case 2: return DrawHLine16(s, x1, y1, x2, colour);
|
|
case 3:
|
|
# if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
|
colour <<= 8;
|
|
# endif
|
|
case 4: return DrawHLine32(s, x1, y1, x2, colour);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int SDL_DrawVLine(SDL_Surface *s, int x1, int y1, int y2, Uint32 colour)
|
|
{
|
|
if (INCLIPX(s, x1) && (INCLIPY(s, y1) || INCLIPY(s, y2)))
|
|
{
|
|
if (y1 > y2) swap(int, y1, y2);
|
|
y1= CLIPY(s, y1);
|
|
y2= CLIPY(s, y2);
|
|
switch (s->format->BytesPerPixel)
|
|
{
|
|
case 1: return DrawVLine8 (s, x1, y1, y2, colour);
|
|
case 2: return DrawVLine16(s, x1, y1, y2, colour);
|
|
case 3:
|
|
# if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
|
colour <<= 8;
|
|
# endif
|
|
case 4: return DrawVLine32(s, x1, y1, y2, colour);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int SDL_DrawLine(SDL_Surface *s, int x1, int y1, int x2, int y2, Uint32 colour)
|
|
{
|
|
if (x1 == x2) return SDL_DrawVLine(s, x1, y1, y2, colour);
|
|
if (y1 == y2) return SDL_DrawHLine(s, x1, y1, x2, colour);
|
|
if (clipLine(s, &x1, &y1, &x2, &y2))
|
|
{
|
|
switch (s->format->BytesPerPixel)
|
|
{
|
|
case 1: return DrawLine8 (s, x1, y1, x2, y2, colour);
|
|
case 2: return DrawLine16(s, x1, y1, x2, y2, colour);
|
|
case 3:
|
|
# if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
|
colour <<= 8;
|
|
# endif
|
|
case 4: return DrawLine32(s, x1, y1, x2, y2, colour);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
/* FillLine */
|
|
/* ---------------------------------------------------------------- */
|
|
|
|
int SDL_FillLine(SDL_Surface *s, int x1, int y1, int x2, int y2, unsigned int w, Uint32 colour)
|
|
{
|
|
if (w == 0) return 0;
|
|
if (w == 1) return SDL_DrawLine(s, x1, y1, x2, y2, colour);
|
|
if (clipLine(s, &x1, &y1, &x2, &y2))
|
|
{
|
|
w >>= 1;
|
|
if (x1 == x2) /* vertical */
|
|
{
|
|
SDL_Point v[4]= { { x1 - w, y1 }, { x1 - w, y2 }, { x1 + w, y2 }, { x1 + w, y1 } };
|
|
return SDL_FillPolygon(s, v, 4, colour);
|
|
}
|
|
if (y1 == y2) /* horizontal */
|
|
{
|
|
SDL_Point v[4]= { { x1, y1 -w }, { x1, y2 + w }, { x1, y2 + w }, { x1, y1 - w } };
|
|
return SDL_FillPolygon(s, v, 4, colour);
|
|
}
|
|
double slope= (double)(y1 - y2) / (double)(x1 - x2);
|
|
double angle= atan(slope);
|
|
double width= (double)w;
|
|
int dx= (int)rint(width * sin(angle));
|
|
int dy= (int)rint(width * cos(angle));
|
|
SDL_Point v[4]= { { x1 - dx, y1 + dy }, { x2 - dx, y2 + dy }, { x2 + dx, y2 - dy }, { x1 + dx, y1 - dy } };
|
|
return SDL_FillPolygon(s, v, 4, colour);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
/* DrawRect */
|
|
/* ---------------------------------------------------------------- */
|
|
|
|
int SDL_DrawRect(SDL_Surface *s, SDL_Rect *rect, Uint32 colour)
|
|
{
|
|
int x1= rect->x, y1= rect->y, x2= x1 + rect->w, y2= y1 + rect->h;
|
|
if (( SDL_DrawLine(s, x1, y1, x1, y2, colour))
|
|
| SDL_DrawLine(s, x1, y2, x2, y2, colour)
|
|
| SDL_DrawLine(s, x2, y2, x2, y1, colour)
|
|
| SDL_DrawLine(s, x2, y1, x1, y1, colour))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
|
|
/* Midpoint circle algorithm.
|
|
* J. R. Van Aken, "An Efficient Ellipse Drawing Algorithm",
|
|
* CG&A 4(9), September 1984, pp. 24--35. */
|
|
|
|
#define DrawCircle(N, M) \
|
|
static inline void xDrawPixel##N(SDL_Surface *s, int x, int y, Uint##M c) \
|
|
{ \
|
|
if (INCLIP(s, x, y)) \
|
|
*((Uint##M *)(s->pixels + (s->pitch * y) + (((M) >> 3) * x)))= c; \
|
|
} \
|
|
\
|
|
int DrawCircle##N(SDL_Surface *s, int x0, int y0, int radius, Uint32 colour) \
|
|
{ \
|
|
int f= 1 - radius; \
|
|
int dx= 0; \
|
|
int dy= -2 * radius; \
|
|
int x= 0; \
|
|
int y= radius; \
|
|
xDrawPixel##M(s, x0, y0 + radius, colour); \
|
|
xDrawPixel##M(s, x0, y0 - radius, colour); \
|
|
xDrawPixel##M(s, x0 + radius, y0, colour); \
|
|
xDrawPixel##M(s, x0 - radius, y0, colour); \
|
|
while (x < y) \
|
|
{ \
|
|
if (f >= 0) \
|
|
{ \
|
|
y--; \
|
|
dy += 2; \
|
|
f += dy; \
|
|
} \
|
|
x++; \
|
|
dx += 2; \
|
|
f += dx + 1; \
|
|
xDrawPixel##M(s, x0 + x, y0 + y, colour); \
|
|
xDrawPixel##M(s, x0 - x, y0 + y, colour); \
|
|
xDrawPixel##M(s, x0 + x, y0 - y, colour); \
|
|
xDrawPixel##M(s, x0 - x, y0 - y, colour); \
|
|
xDrawPixel##M(s, x0 + y, y0 + x, colour); \
|
|
xDrawPixel##M(s, x0 - y, y0 + x, colour); \
|
|
xDrawPixel##M(s, x0 + y, y0 - x, colour); \
|
|
xDrawPixel##M(s, x0 - y, y0 - x, colour); \
|
|
} \
|
|
return 0; \
|
|
}
|
|
|
|
DrawCircle( 8, 8)
|
|
DrawCircle(16, 16)
|
|
DrawCircle(32, 32)
|
|
|
|
#undef DrawCircle
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
/* DrawCircle */
|
|
/* ---------------------------------------------------------------- */
|
|
|
|
int SDL_DrawCircle(SDL_Surface *s, int x0, int y0, int radius, Uint32 colour)
|
|
{
|
|
if (radius > 0
|
|
&& (x0 + radius >= CLIPX0(s) || CLIPX1(s) - 1 <= x0 - radius)
|
|
&& (y0 + radius >= CLIPY0(s) || CLIPY1(s) - 1 <= y0 - radius))
|
|
{
|
|
switch (s->format->BytesPerPixel)
|
|
{
|
|
case 1: return DrawCircle8 (s, x0, y0, radius, colour);
|
|
case 2: return DrawCircle16(s, x0, y0, radius, colour);
|
|
case 3:
|
|
# if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
|
colour <<= 8;
|
|
# endif
|
|
case 4: return DrawCircle32(s, x0, y0, radius, colour);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
/* FillCircle */
|
|
/* ---------------------------------------------------------------- */
|
|
|
|
/* See DrawCircle above. */
|
|
|
|
#define FillCircle(N, M) \
|
|
static void xDrawLine##N(SDL_Surface *s, int x0, int x1, int y, Uint##M c) \
|
|
{ \
|
|
if (!INCLIPY(s, y)) return; \
|
|
if (x0 < CLIPX0(s)) x0= CLIPX0(s); \
|
|
if (x1 >= CLIPX1(s)) x1= CLIPX1(s) - 1; \
|
|
Uint##M *p= (Uint##M *)(s->pixels + s->pitch * y + ((M) >> 3) * x0); \
|
|
x1 -= x0; \
|
|
while (x1-- >= 0) \
|
|
*p++= c; \
|
|
} \
|
|
\
|
|
int FillCircle##N(SDL_Surface *s, int x0, int y0, int radius, Uint##M colour) \
|
|
{ \
|
|
int f= 1 - radius; \
|
|
int ddF_x= 0; \
|
|
int ddF_y= -2 * radius; \
|
|
int x= 0; \
|
|
int y= radius; \
|
|
xDrawPixel##N(s, x0, y0 + radius, colour); \
|
|
xDrawPixel##N(s, x0, y0 - radius, colour); \
|
|
xDrawLine##N(s, x0 - radius, x0 + radius, y0, colour); \
|
|
while (x < y) \
|
|
{ \
|
|
if (f >= 0) \
|
|
{ \
|
|
y--; \
|
|
ddF_y += 2; \
|
|
f += ddF_y; \
|
|
} \
|
|
x++; \
|
|
ddF_x += 2; \
|
|
f += ddF_x + 1; \
|
|
xDrawLine##N(s, x0 - x, x0 + x, y0 + y, colour); \
|
|
xDrawLine##N(s, x0 - x, x0 + x, y0 - y, colour); \
|
|
xDrawLine##N(s, x0 - y, x0 + y, y0 + x, colour); \
|
|
xDrawLine##N(s, x0 - y, x0 + y, y0 - x, colour); \
|
|
} \
|
|
return 0; \
|
|
}
|
|
|
|
FillCircle( 8, 8)
|
|
FillCircle(16, 16)
|
|
FillCircle(32, 32)
|
|
|
|
#undef FillCircle
|
|
|
|
int SDL_FillCircle(SDL_Surface *s, int x0, int y0, int radius, Uint32 colour)
|
|
{
|
|
if (radius > 0
|
|
&& (x0 + radius >= CLIPX0(s) || CLIPX1(s) - 1 <= x0 - radius)
|
|
&& (y0 + radius >= CLIPY0(s) || CLIPY1(s) - 1 <= y0 - radius))
|
|
{
|
|
switch (s->format->BytesPerPixel)
|
|
{
|
|
case 1: return FillCircle8 (s, x0, y0, radius, colour);
|
|
case 2: return FillCircle16(s, x0, y0, radius, colour);
|
|
case 3:
|
|
# if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
|
|
colour <<= 8;
|
|
# endif
|
|
case 4: return FillCircle32(s, x0, y0, radius, colour);
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
/* DrawPolygon */
|
|
/* ---------------------------------------------------------------- */
|
|
|
|
int SDL_DrawPolygon(SDL_Surface *s, SDL_Point *v, int n, Uint32 c)
|
|
{
|
|
if (n == 0) return 0;
|
|
if (n == 1) return SDL_DrawPixel(s, v->x, v->y, c);
|
|
int i;
|
|
for (i= 1; i < n; ++i)
|
|
SDL_DrawLine(s, v[i-1].x, v[i-1].y, v[i].x, v[i].y, c);
|
|
SDL_DrawLine(s, v[n-1].x, v[n-1].y, v[0].x, v[0].y, c);
|
|
return 0;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------- */
|
|
/* FillPolygon */
|
|
/* ---------------------------------------------------------------- */
|
|
|
|
/* Trivial scan-line fill algorithm. For each scan line intersecting
|
|
* the polygon, make an LR ordered list of crossing points. Successive
|
|
* pairs of points define runs of pixels lying within the polygon. */
|
|
|
|
int SDL_FillPolygon(SDL_Surface *s, SDL_Point *v, int n, Uint32 c)
|
|
{
|
|
if (n == 0) return 0;
|
|
if (n == 1) return SDL_DrawPixel(s, v->x, v->y, c);
|
|
int nxs, *xs= alloca(sizeof(int) * n);
|
|
int y, i, j, k;
|
|
int y0= v[0].y, y1= y0;
|
|
for (i= 1; i < n; ++i)
|
|
{
|
|
y= v[i].y;
|
|
if (y < y0) y0= y;
|
|
if (y > y1) y1= y;
|
|
}
|
|
if (y0 < CLIPY0(s)) y0= CLIPY0(s);
|
|
if (y1 >= CLIPY1(s)) y1= CLIPY1(s) - 1;
|
|
for (y= y0; y <= y1; ++y)
|
|
{
|
|
nxs= 0;
|
|
j= n - 1;
|
|
for (i= 0; i < n; j= i++)
|
|
if ((v[i].y < y && y <= v[j].y) || (v[j].y < y && y <= v[i].y))
|
|
{
|
|
xs[nxs++]= (int)rint(v[i].x + ((double)y - v[i].y) / ((double)v[j].y - v[i].y) * ((double)v[j].x - v[i].x));
|
|
for (k= nxs - 1; k && xs[k-1] > xs[k]; --k)
|
|
swap(int, xs[k-1], xs[k]);
|
|
}
|
|
for (i= 0; i < nxs; i += 2)
|
|
SDL_DrawHLine(s, xs[i], y, xs[i+1], c);
|
|
}
|
|
return 0;
|
|
}
|