FreeBASIC  0.91.0
gfx_circle.c
Go to the documentation of this file.
1 /* CIRCLE statement */
2 
3 #include "fb_gfx.h"
4 #include <math.h>
5 
6 
7 /*:::::*/
8 static void draw_scanline(FB_GFXCTX *ctx, int y, int x1, int x2, unsigned int color, int fill, char *filled)
9 {
10  if ((y >= ctx->view_y) && (y < ctx->view_y + ctx->view_h)) {
11  if (fill) {
12  if ((x2 < ctx->view_x) || (x1 >= ctx->view_x + ctx->view_w) || (filled[y - ctx->view_y]))
13  return;
14  filled[y - ctx->view_y] = TRUE;
15  x1 = MAX(x1, ctx->view_x);
16  x2 = MIN(x2, ctx->view_x + ctx->view_w - 1);
17  ctx->pixel_set(ctx->line[y] + (x1 * ctx->target_bpp), color, x2 - x1 + 1);
18  }
19  else {
20  if ((x1 >= ctx->view_x) && (x1 < ctx->view_x + ctx->view_w))
21  ctx->put_pixel(ctx, x1, y, color);
22  if ((x2 >= ctx->view_x) && (x2 < ctx->view_x + ctx->view_w))
23  ctx->put_pixel(ctx, x2, y, color);
24  }
25  }
26 }
27 
28 
29 static void draw_ellipse
30  (
31  FB_GFXCTX *ctx,
32  int x,
33  int y,
34  float a,
35  float b,
36  unsigned int color,
37  int fill,
38  int *top,
39  int *bottom
40  )
41 {
42  int d, x1, y1, x2, y2;
43  long long dx, dy, aq, bq, r, rx, ry;
44  char filled[ctx->view_h];
45 
46  x1 = x - a;
47  x2 = x + a;
48  y1 = y2 = y;
49  fb_hMemSet(filled, 0, ctx->view_h);
50 
51  if (!b) {
52  draw_scanline(ctx, y, x1, x2, color, TRUE, filled);
53  *top = y;
54  *bottom = y;
55  return;
56  }
57 
58  draw_scanline(ctx, y, x1, x2, color, fill, filled);
59 
60  aq = a * a;
61  bq = b * b;
62  dx = aq << 1;
63  dy = bq << 1;
64  r = a * bq;
65  rx = r << 1;
66  ry = 0;
67  d = a;
68 
69  while (d > 0) {
70  if (r > 0) {
71  y1++;
72  y2--;
73  ry += dx;
74  r -= ry;
75  }
76  if (r <= 0) {
77  d--;
78  x1++;
79  x2--;
80  rx -= dy;
81  r += rx;
82  }
83  draw_scanline(ctx, y1, x1, x2, color, fill, filled);
84  draw_scanline(ctx, y2, x1, x2, color, fill, filled);
85  }
86 
87  /* Tell caller exactly which rows were drawn so the SET_DIRTY() will be
88  correct */
89  *top = y2;
90  *bottom = y1;
91 }
92 
93 /*:::::*/
94 static void get_arc_point(float angle, float a, float b, int *x, int *y)
95 {
96  float c, s;
97 
98  c = cos(angle) * a;
99  s = sin(angle) * b;
100  if (c >= 0)
101  *x = (int)(c + 0.5);
102  else
103  *x = (int)(c - 0.5);
104  if (s >= 0)
105  *y = (int)(s + 0.5);
106  else
107  *y = (int)(s - 0.5);
108 }
109 
110 FBCALL void fb_GfxEllipse(void *target, float fx, float fy, float radius, unsigned int color, float aspect, float start, float end, int fill, int flags)
111 {
113  int x, y, x1, y1, top, bottom;
114  unsigned int orig_color;
115  float a, b, orig_x, orig_y, increment;
116 
117  if (!__fb_gfx || radius <= 0.0)
118  return;
119 
120  orig_x = fx;
121  orig_y = fy;
122 
123  fb_hPrepareTarget(context, target);
124 
125  orig_color = color;
126  if (flags & DEFAULT_COLOR_1)
127  color = context->fg_color;
128  else
129  color = fb_hFixColor(context->target_bpp, color);
130 
131  fb_hSetPixelTransfer(context, color);
132 
133  fb_hFixRelative(context, flags, &fx, &fy, NULL, NULL);
134 
135  fb_hTranslateCoord(context, fx, fy, &x, &y);
136 
137  if (context->flags & CTX_WINDOW_ACTIVE) {
138  /* radius gets multiplied by the VIEW/WINDOW width ratio (aspect is unchanged) */
139  radius *= (context->view_w / context->win_w);
140  }
141 
142  if (aspect == 0.0)
143  aspect = __fb_gfx->aspect;
144 
145  if (aspect > 1.0) {
146  a = (radius / aspect);
147  b = radius;
148  }
149  else {
150  a = radius;
151  b = (radius * aspect);
152  }
153 
154  if ((start != 0.0) || (end != 3.141593f * 2.0)) {
155  if (start < 0) {
156  start = -start;
157  get_arc_point(start, a, b, &x1, &y1);
158  x1 = orig_x + x1;
159  y1 = orig_y - y1;
160  fb_GfxLine(target, orig_x, orig_y, x1, y1, orig_color, LINE_TYPE_LINE, 0xFFFF, COORD_TYPE_AA | (flags & ~COORD_TYPE_MASK));
161  }
162  if (end < 0) {
163  end = -end;
164  get_arc_point(end, a, b, &x1, &y1);
165  x1 = orig_x + x1;
166  y1 = orig_y - y1;
167  fb_GfxLine(target, orig_x, orig_y, x1, y1, orig_color, LINE_TYPE_LINE, 0xFFFF, COORD_TYPE_AA | (flags & ~COORD_TYPE_MASK));
168  }
169 
170  while (end < start)
171  end += 2 * PI;
172  while (end - start > 2 * PI)
173  start += 2 * PI;
174 
175  increment = 1 / (sqrt(a) * sqrt(b) * 1.5);
176 
177  DRIVER_LOCK();
178 
179  top = bottom = y;
180  for (; start < end + (increment / 2); start += increment) {
181  get_arc_point(start, a, b, &x1, &y1);
182  x1 = x + x1;
183  y1 = y - y1;
184  if ((x1 < context->view_x) || (x1 >= context->view_x + context->view_w) ||
185  (y1 < context->view_y) || (y1 >= context->view_y + context->view_h))
186  continue;
187  context->put_pixel(context, x1, y1, color);
188  if (y1 > bottom)
189  bottom = y1;
190  if (y1 < top)
191  top = y1;
192  }
193  }
194  else {
195  DRIVER_LOCK();
196  draw_ellipse(context, x, y, a, b, color, fill, &top, &bottom);
197  }
198 
199  top = MID(context->view_y, top, context->view_y + context->view_h - 1);
200  bottom = MID(context->view_y, bottom, context->view_y + context->view_h - 1);
201  if( top > bottom )
202  SWAP( top, bottom );
203 
204  SET_DIRTY(context, top, bottom - top + 1);
205 
206  DRIVER_UNLOCK();
207 }