1.002 release
[imager.git] / draw.c
CommitLineData
af22c916 1#define IMAGER_NO_CONTEXT
92bda632 2#include "imager.h"
02d1d628
AMH
3#include "draw.h"
4#include "log.h"
92bda632 5#include "imageri.h"
9b1ec2b8 6#include "imrender.h"
6af18d2b 7#include <limits.h>
1bc6916b
TC
8#define NDEBUG
9#include <assert.h>
6af18d2b 10
40068b33
TC
11int
12i_ppix_norm(i_img *im, i_img_dim x, i_img_dim y, i_color const *col) {
13 i_color src;
14 i_color work;
15 int dest_alpha;
16 int remains;
17
18 if (!col->channel[3])
19 return 0;
20
21 switch (im->channels) {
22 case 1:
23 work = *col;
24 i_adapt_colors(2, 4, &work, 1);
25 i_gpix(im, x, y, &src);
26 remains = 255 - work.channel[1];
27 src.channel[0] = (src.channel[0] * remains
28 + work.channel[0] * work.channel[1]) / 255;
29 return i_ppix(im, x, y, &src);
30
31 case 2:
32 work = *col;
33 i_adapt_colors(2, 4, &work, 1);
34 i_gpix(im, x, y, &src);
8d14daab 35 remains = 255 - work.channel[1];
40068b33
TC
36 dest_alpha = work.channel[1] + remains * src.channel[1] / 255;
37 if (work.channel[1] == 255) {
38 return i_ppix(im, x, y, &work);
39 }
40 else {
41 src.channel[0] = (work.channel[1] * work.channel[0]
42 + remains * src.channel[0] * src.channel[1] / 255) / dest_alpha;
43 src.channel[1] = dest_alpha;
44 return i_ppix(im, x, y, &src);
45 }
46
47 case 3:
48 work = *col;
49 i_gpix(im, x, y, &src);
50 remains = 255 - work.channel[3];
51 src.channel[0] = (src.channel[0] * remains
52 + work.channel[0] * work.channel[3]) / 255;
53 src.channel[1] = (src.channel[1] * remains
54 + work.channel[1] * work.channel[3]) / 255;
55 src.channel[2] = (src.channel[2] * remains
56 + work.channel[2] * work.channel[3]) / 255;
57 return i_ppix(im, x, y, &src);
58
59 case 4:
60 work = *col;
61 i_gpix(im, x, y, &src);
8d14daab 62 remains = 255 - work.channel[3];
40068b33
TC
63 dest_alpha = work.channel[3] + remains * src.channel[3] / 255;
64 if (work.channel[3] == 255) {
65 return i_ppix(im, x, y, &work);
66 }
67 else {
68 src.channel[0] = (work.channel[3] * work.channel[0]
69 + remains * src.channel[0] * src.channel[3] / 255) / dest_alpha;
70 src.channel[1] = (work.channel[3] * work.channel[1]
71 + remains * src.channel[1] * src.channel[3] / 255) / dest_alpha;
72 src.channel[2] = (work.channel[3] * work.channel[2]
73 + remains * src.channel[2] * src.channel[3] / 255) / dest_alpha;
74 src.channel[3] = dest_alpha;
75 return i_ppix(im, x, y, &src);
76 }
77 }
78 return 0;
79}
80
3efb0915
TC
81static void
82cfill_from_btm(i_img *im, i_fill_t *fill, struct i_bitmap *btm,
8d14daab 83 i_img_dim bxmin, i_img_dim bxmax, i_img_dim bymin, i_img_dim bymax);
3efb0915 84
02d1d628 85void
8d14daab
TC
86i_mmarray_cr(i_mmarray *ar,i_img_dim l) {
87 i_img_dim i;
88 size_t alloc_size;
02d1d628
AMH
89
90 ar->lines=l;
f0960b14
TC
91 alloc_size = sizeof(minmax) * l;
92 /* check for overflow */
93 if (alloc_size / l != sizeof(minmax)) {
94 fprintf(stderr, "overflow calculating memory allocation");
95 exit(3);
96 }
97 ar->data=mymalloc(alloc_size); /* checked 5jul05 tonyc */
498a7bc2
TC
98 for(i=0;i<l;i++) {
99 ar->data[i].max = -1;
100 ar->data[i].min = i_img_dim_MAX;
101 }
02d1d628
AMH
102}
103
104void
105i_mmarray_dst(i_mmarray *ar) {
106 ar->lines=0;
107 if (ar->data != NULL) { myfree(ar->data); ar->data=NULL; }
108}
109
110void
8d14daab 111i_mmarray_add(i_mmarray *ar,i_img_dim x,i_img_dim y) {
02d1d628
AMH
112 if (y>-1 && y<ar->lines)
113 {
114 if (x<ar->data[y].min) ar->data[y].min=x;
115 if (x>ar->data[y].max) ar->data[y].max=x;
116 }
117}
118
498a7bc2 119i_img_dim
8d14daab 120i_mmarray_gmin(i_mmarray *ar,i_img_dim y) {
02d1d628
AMH
121 if (y>-1 && y<ar->lines) return ar->data[y].min;
122 else return -1;
123}
124
498a7bc2 125i_img_dim
8d14daab 126i_mmarray_getm(i_mmarray *ar,i_img_dim y) {
498a7bc2
TC
127 if (y>-1 && y<ar->lines)
128 return ar->data[y].max;
129 else
130 return i_img_dim_MAX;
02d1d628
AMH
131}
132
8d14daab
TC
133#if 0
134/* unused? */
02d1d628
AMH
135void
136i_mmarray_render(i_img *im,i_mmarray *ar,i_color *val) {
8d14daab 137 i_img_dim i,x;
02d1d628
AMH
138 for(i=0;i<ar->lines;i++) if (ar->data[i].max!=-1) for(x=ar->data[i].min;x<ar->data[i].max;x++) i_ppix(im,x,i,val);
139}
8d14daab 140#endif
02d1d628 141
02d1d628
AMH
142static
143void
8d14daab 144i_arcdraw(i_img_dim x1, i_img_dim y1, i_img_dim x2, i_img_dim y2, i_mmarray *ar) {
02d1d628
AMH
145 double alpha;
146 double dsec;
8d14daab 147 i_img_dim temp;
02d1d628 148 alpha=(double)(y2-y1)/(double)(x2-x1);
b254292b 149 if (fabs(alpha) <= 1)
02d1d628
AMH
150 {
151 if (x2<x1) { temp=x1; x1=x2; x2=temp; temp=y1; y1=y2; y2=temp; }
152 dsec=y1;
b254292b 153 while(x1<=x2)
02d1d628 154 {
8d14daab 155 i_mmarray_add(ar,x1,(i_img_dim)(dsec+0.5));
b254292b 156 dsec+=alpha;
02d1d628
AMH
157 x1++;
158 }
159 }
160 else
161 {
162 alpha=1/alpha;
163 if (y2<y1) { temp=x1; x1=x2; x2=temp; temp=y1; y1=y2; y2=temp; }
164 dsec=x1;
b254292b 165 while(y1<=y2)
02d1d628 166 {
8d14daab 167 i_mmarray_add(ar,(i_img_dim)(dsec+0.5),y1);
b254292b 168 dsec+=alpha;
02d1d628
AMH
169 y1++;
170 }
171 }
172}
173
174void
175i_mmarray_info(i_mmarray *ar) {
8d14daab 176 i_img_dim i;
02d1d628 177 for(i=0;i<ar->lines;i++)
8d14daab
TC
178 if (ar->data[i].max!=-1)
179 printf("line %"i_DF ": min=%" i_DF ", max=%" i_DF ".\n",
180 i_DFc(i), i_DFc(ar->data[i].min), i_DFc(ar->data[i].max));
02d1d628
AMH
181}
182
a8652edf 183static void
8d14daab 184i_arc_minmax(i_int_hlines *hlines,i_img_dim x,i_img_dim y, double rad,float d1,float d2) {
02d1d628 185 i_mmarray dot;
98747309 186 double f;
8d14daab 187 i_img_dim x1,y1;
02d1d628 188
a8652edf 189 i_mmarray_cr(&dot, hlines->limit_y);
02d1d628 190
8d14daab
TC
191 x1=(i_img_dim)(x+0.5+rad*cos(d1*PI/180.0));
192 y1=(i_img_dim)(y+0.5+rad*sin(d1*PI/180.0));
02d1d628
AMH
193
194 /* printf("x1: %d.\ny1: %d.\n",x1,y1); */
195 i_arcdraw(x, y, x1, y1, &dot);
196
8d14daab
TC
197 x1=(i_img_dim)(x+0.5+rad*cos(d2*PI/180.0));
198 y1=(i_img_dim)(y+0.5+rad*sin(d2*PI/180.0));
02d1d628 199
8d14daab
TC
200 for(f=d1;f<=d2;f+=0.01)
201 i_mmarray_add(&dot,(i_img_dim)(x+0.5+rad*cos(f*PI/180.0)),(i_img_dim)(y+0.5+rad*sin(f*PI/180.0)));
6af18d2b 202
02d1d628
AMH
203 /* printf("x1: %d.\ny1: %d.\n",x1,y1); */
204 i_arcdraw(x, y, x1, y1, &dot);
205
a8652edf
TC
206 /* render the minmax values onto the hlines */
207 for (y = 0; y < dot.lines; y++) {
208 if (dot.data[y].max!=-1) {
8d14daab 209 i_img_dim minx, width;
a8652edf
TC
210 minx = dot.data[y].min;
211 width = dot.data[y].max - dot.data[y].min + 1;
212 i_int_hlines_add(hlines, y, minx, width);
213 }
214 }
215
02d1d628 216 /* dot.info(); */
7f882a01 217 i_mmarray_dst(&dot);
02d1d628
AMH
218}
219
a8652edf 220static void
8d14daab 221i_arc_hlines(i_int_hlines *hlines,i_img_dim x,i_img_dim y,double rad,float d1,float d2) {
a8652edf
TC
222 if (d1 <= d2) {
223 i_arc_minmax(hlines, x, y, rad, d1, d2);
224 }
225 else {
226 i_arc_minmax(hlines, x, y, rad, d1, 360);
227 i_arc_minmax(hlines, x, y, rad, 0, d2);
228 }
229}
230
92bda632
TC
231/*
232=item i_arc(im, x, y, rad, d1, d2, color)
233
234=category Drawing
235=synopsis i_arc(im, 50, 50, 20, 45, 135, &color);
236
237Fills an arc centered at (x,y) with radius I<rad> covering the range
238of angles in degrees from d1 to d2, with the color.
239
240=cut
241*/
242
a8652edf 243void
8d14daab 244i_arc(i_img *im, i_img_dim x, i_img_dim y,double rad,double d1,double d2,const i_color *val) {
a8652edf 245 i_int_hlines hlines;
857e686a
TC
246 dIMCTXim(im);
247
248 im_log((aIMCTX,1,"i_arc(im %p,(x,y)=(" i_DFp "), rad %f, d1 %f, d2 %f, col %p)",
249 im, i_DFcp(x, y), rad, d1, d2, val));
a8652edf
TC
250
251 i_int_init_hlines_img(&hlines, im);
252
253 i_arc_hlines(&hlines, x, y, rad, d1, d2);
254
255 i_int_hlines_fill_color(im, &hlines, val);
256
257 i_int_hlines_destroy(&hlines);
258}
259
92bda632
TC
260/*
261=item i_arc_cfill(im, x, y, rad, d1, d2, fill)
262
263=category Drawing
264=synopsis i_arc_cfill(im, 50, 50, 35, 90, 135, fill);
265
266Fills an arc centered at (x,y) with radius I<rad> covering the range
267of angles in degrees from d1 to d2, with the fill object.
268
269=cut
270*/
271
a8652edf
TC
272#define MIN_CIRCLE_STEPS 8
273#define MAX_CIRCLE_STEPS 360
274
f1ac5027 275void
8d14daab 276i_arc_cfill(i_img *im, i_img_dim x, i_img_dim y,double rad,double d1,double d2,i_fill_t *fill) {
a8652edf 277 i_int_hlines hlines;
857e686a
TC
278 dIMCTXim(im);
279
280 im_log((aIMCTX,1,"i_arc_cfill(im %p,(x,y)=(" i_DFp "), rad %f, d1 %f, d2 %f, fill %p)",
281 im, i_DFcp(x, y), rad, d1, d2, fill));
f1ac5027 282
a8652edf 283 i_int_init_hlines_img(&hlines, im);
f1ac5027 284
a8652edf 285 i_arc_hlines(&hlines, x, y, rad, d1, d2);
f1ac5027 286
a8652edf 287 i_int_hlines_fill_fill(im, &hlines, fill);
f1ac5027 288
a8652edf
TC
289 i_int_hlines_destroy(&hlines);
290}
f1ac5027 291
a8652edf
TC
292static void
293arc_poly(int *count, double **xvals, double **yvals,
294 double x, double y, double rad, double d1, double d2) {
295 double d1_rad, d2_rad;
296 double circum;
8d14daab 297 i_img_dim steps, point_count;
a8652edf
TC
298 double angle_inc;
299
300 /* normalize the angles */
301 d1 = fmod(d1, 360);
302 if (d1 == 0) {
303 if (d2 >= 360) { /* default is 361 */
304 d2 = 360;
305 }
306 else {
307 d2 = fmod(d2, 360);
308 if (d2 < d1)
309 d2 += 360;
310 }
311 }
312 else {
313 d2 = fmod(d2, 360);
314 if (d2 < d1)
315 d2 += 360;
316 }
317 d1_rad = d1 * PI / 180;
318 d2_rad = d2 * PI / 180;
319
320 /* how many segments for the curved part?
321 we do a maximum of one per degree, with a minimum of 8/circle
322 we try to aim at having about one segment per 2 pixels
323 Work it out per circle to get a step size.
324
325 I was originally making steps = circum/2 but that looked horrible.
326
327 I think there might be an issue in the polygon filler.
328 */
329 circum = 2 * PI * rad;
330 steps = circum;
331 if (steps > MAX_CIRCLE_STEPS)
332 steps = MAX_CIRCLE_STEPS;
333 else if (steps < MIN_CIRCLE_STEPS)
334 steps = MIN_CIRCLE_STEPS;
335
336 angle_inc = 2 * PI / steps;
337
338 point_count = steps + 5; /* rough */
e310e5f9
TC
339 /* point_count is always relatively small, so allocation won't overflow */
340 *xvals = mymalloc(point_count * sizeof(double)); /* checked 17feb2005 tonyc */
341 *yvals = mymalloc(point_count * sizeof(double)); /* checked 17feb2005 tonyc */
a8652edf
TC
342
343 /* from centre to edge at d1 */
344 (*xvals)[0] = x;
345 (*yvals)[0] = y;
346 (*xvals)[1] = x + rad * cos(d1_rad);
347 (*yvals)[1] = y + rad * sin(d1_rad);
348 *count = 2;
349
350 /* step around the curve */
351 while (d1_rad < d2_rad) {
352 (*xvals)[*count] = x + rad * cos(d1_rad);
353 (*yvals)[*count] = y + rad * sin(d1_rad);
354 ++*count;
355 d1_rad += angle_inc;
356 }
f1ac5027 357
a8652edf
TC
358 /* finish off the curve */
359 (*xvals)[*count] = x + rad * cos(d2_rad);
360 (*yvals)[*count] = y + rad * sin(d2_rad);
361 ++*count;
362}
f1ac5027 363
92bda632
TC
364/*
365=item i_arc_aa(im, x, y, rad, d1, d2, color)
366
367=category Drawing
368=synopsis i_arc_aa(im, 50, 50, 35, 90, 135, &color);
369
5715f7c3 370Anti-alias fills an arc centered at (x,y) with radius I<rad> covering
92bda632
TC
371the range of angles in degrees from d1 to d2, with the color.
372
373=cut
374*/
375
a8652edf
TC
376void
377i_arc_aa(i_img *im, double x, double y, double rad, double d1, double d2,
97ac0a96 378 const i_color *val) {
a8652edf
TC
379 double *xvals, *yvals;
380 int count;
857e686a
TC
381 dIMCTXim(im);
382
383 im_log((aIMCTX,1,"i_arc_aa(im %p,(x,y)=(%f,%f), rad %f, d1 %f, d2 %f, col %p)",
384 im, x, y, rad, d1, d2, val));
a8652edf 385
6b8fe08b 386 arc_poly(&count, &xvals, &yvals, x, y, rad, d1, d2);
a8652edf
TC
387
388 i_poly_aa(im, count, xvals, yvals, val);
389
390 myfree(xvals);
391 myfree(yvals);
f1ac5027
TC
392}
393
92bda632
TC
394/*
395=item i_arc_aa_cfill(im, x, y, rad, d1, d2, fill)
396
397=category Drawing
398=synopsis i_arc_aa_cfill(im, 50, 50, 35, 90, 135, fill);
399
5715f7c3 400Anti-alias fills an arc centered at (x,y) with radius I<rad> covering
92bda632
TC
401the range of angles in degrees from d1 to d2, with the fill object.
402
403=cut
404*/
405
a8652edf
TC
406void
407i_arc_aa_cfill(i_img *im, double x, double y, double rad, double d1, double d2,
408 i_fill_t *fill) {
409 double *xvals, *yvals;
410 int count;
857e686a
TC
411 dIMCTXim(im);
412
413 im_log((aIMCTX,1,"i_arc_aa_cfill(im %p,(x,y)=(%f,%f), rad %f, d1 %f, d2 %f, fill %p)",
414 im, x, y, rad, d1, d2, fill));
a8652edf
TC
415
416 arc_poly(&count, &xvals, &yvals, x, y, rad, d1, d2);
417
418 i_poly_aa_cfill(im, count, xvals, yvals, fill);
6af18d2b 419
a8652edf
TC
420 myfree(xvals);
421 myfree(yvals);
422}
6af18d2b 423
8d14daab
TC
424typedef i_img_dim frac;
425static frac float_to_frac(double x) { return (frac)(0.5+x*16.0); }
6af18d2b 426
bf18ef3a
TC
427typedef void
428(*flush_render_t)(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_sample_t *cover, void *ctx);
429
430static void
431i_circle_aa_low(i_img *im, double x, double y, double rad, flush_render_t r, void *ctx);
432
433static void
434scanline_flush_color(i_img *im, i_img_dim l, i_img_dim y, i_img_dim width, const i_sample_t *cover, void *ctx);
435
436static void
437scanline_flush_fill(i_img *im, i_img_dim l, i_img_dim y, i_img_dim width, const i_sample_t *cover, void *ctx);
438
439typedef struct {
440 i_render r;
441 i_color c;
442} flush_color_t;
443
444typedef struct {
445 i_render r;
446 i_fill_t *fill;
447} flush_fill_t;
448
92bda632
TC
449/*
450=item i_circle_aa(im, x, y, rad, color)
451
452=category Drawing
453=synopsis i_circle_aa(im, 50, 50, 45, &color);
454
5715f7c3 455Anti-alias fills a circle centered at (x,y) for radius I<rad> with
92bda632
TC
456color.
457
458=cut
459*/
bf18ef3a 460
6af18d2b 461void
8d14daab 462i_circle_aa(i_img *im, double x, double y, double rad, const i_color *val) {
bf18ef3a
TC
463 flush_color_t fc;
464
465 fc.c = *val;
466 i_render_init(&fc.r, im, rad * 2 + 1);
467
468 i_circle_aa_low(im, x, y, rad, scanline_flush_color, &fc);
469
470 i_render_done(&fc.r);
471}
472
473/*
474=item i_circle_aa_fill(im, x, y, rad, fill)
475
476=category Drawing
477=synopsis i_circle_aa_fill(im, 50, 50, 45, fill);
478
479Anti-alias fills a circle centered at (x,y) for radius I<rad> with
480fill.
481
482=cut
483*/
484
485void
486i_circle_aa_fill(i_img *im, double x, double y, double rad, i_fill_t *fill) {
487 flush_fill_t ff;
488
489 ff.fill = fill;
490 i_render_init(&ff.r, im, rad * 2 + 1);
491
492 i_circle_aa_low(im, x, y, rad, scanline_flush_fill, &ff);
493
494 i_render_done(&ff.r);
495}
496
497static void
498i_circle_aa_low(i_img *im, double x, double y, double rad, flush_render_t r,
499 void *ctx) {
6af18d2b 500 i_color temp;
8d14daab 501 i_img_dim ly;
af22c916 502 dIMCTXim(im);
1bc6916b
TC
503 i_img_dim first_row = floor(y) - ceil(rad);
504 i_img_dim last_row = ceil(y) + ceil(rad);
505 double r_sqr = rad * rad;
bf18ef3a
TC
506 i_img_dim max_width = 2 * ceil(rad) + 1;
507 unsigned char *coverage = NULL;
508 size_t coverage_size;
1bc6916b 509 int sub;
6af18d2b 510
bf18ef3a
TC
511 im_log((aIMCTX, 1, "i_circle_aa_low(im %p, centre(" i_DFp "), rad %.2f, r %p, ctx %p)\n",
512 im, i_DFcp(x, y), rad, r, ctx));
6af18d2b 513
1bc6916b
TC
514 if (first_row < 0)
515 first_row = 0;
516 if (last_row > im->ysize-1)
517 last_row = im->ysize - 1;
6af18d2b 518
1bc6916b
TC
519 if (rad <= 0 || last_row < first_row) {
520 /* outside the image */
521 return;
522 }
6af18d2b 523
bf18ef3a
TC
524 coverage_size = max_width;
525 coverage = mymalloc(coverage_size);
1bc6916b
TC
526
527 for(ly = first_row; ly < last_row; ly++) {
528 frac min_frac_x[16];
529 frac max_frac_x[16];
530 i_img_dim min_frac_left_x = im->xsize * 16;
531 i_img_dim max_frac_left_x = -1;
532 i_img_dim min_frac_right_x = im->xsize * 16;
533 i_img_dim max_frac_right_x = -1;
534 /* reset work_y each row so the error doesn't build up */
535 double work_y = ly;
536 double dy, dy_sqr;
537
538 for (sub = 0; sub < 16; ++sub) {
539 work_y += 1.0 / 16.0;
540 dy = work_y - y;
541 dy_sqr = dy * dy;
542
543 if (dy_sqr < r_sqr) {
544 double dx = sqrt(r_sqr - dy_sqr);
545 double left_x = x - dx;
546 double right_x = x + dx;
547 frac frac_left_x = float_to_frac(left_x);
548 frac frac_right_x = float_to_frac(right_x);
549
550 if (frac_left_x < min_frac_left_x)
551 min_frac_left_x = frac_left_x;
552 if (frac_left_x > max_frac_left_x)
553 max_frac_left_x = frac_left_x;
554 if (frac_right_x < min_frac_right_x)
555 min_frac_right_x = frac_right_x;
556 if (frac_right_x > max_frac_right_x)
557 max_frac_right_x = frac_right_x;
558 min_frac_x[sub] = frac_left_x;
559 max_frac_x[sub] = frac_right_x;
560 }
561 else {
562 min_frac_x[sub] = max_frac_x[sub] = 0;
563 max_frac_left_x = im->xsize * 16;
564 min_frac_right_x = -1;
565 }
6af18d2b
AMH
566 }
567
1bc6916b
TC
568 if (min_frac_left_x != -1) {
569 /* something to draw on this line */
570 i_img_dim min_x = (min_frac_left_x / 16);
571 i_img_dim max_x = (max_frac_right_x + 15) / 16;
572 i_img_dim left_solid = (max_frac_left_x + 15) / 16;
573 i_img_dim right_solid = min_frac_right_x / 16;
574 i_img_dim work_x;
575 i_img_dim frac_work_x;
bf18ef3a 576 i_sample_t *cout = coverage;
1bc6916b
TC
577
578 for (work_x = min_x, frac_work_x = min_x * 16;
579 work_x <= max_x;
580 ++work_x, frac_work_x += 16) {
581 if (work_x <= left_solid || work_x >= right_solid) {
bf18ef3a 582 int pix_coverage = 0;
1bc6916b
TC
583 int ch;
584 double ratio;
585 i_img_dim frac_work_right = frac_work_x + 16;
586 for (sub = 0; sub < 16; ++sub) {
587 frac pix_left = min_frac_x[sub];
588 frac pix_right = max_frac_x[sub];
589 if (pix_left < pix_right
590 && pix_left < frac_work_right
591 && pix_right >= frac_work_x) {
592 if (pix_left < frac_work_x)
593 pix_left = frac_work_x;
594 if (pix_right > frac_work_right)
595 pix_right = frac_work_right;
bf18ef3a 596 pix_coverage += pix_right - pix_left;
1bc6916b
TC
597 }
598 }
599
bf18ef3a
TC
600 assert(pix_coverage <= 256);
601 *cout++ = pix_coverage * 255 / 256;
1bc6916b
TC
602 }
603 else {
604 /* full coverage */
bf18ef3a 605 *cout++ = 255;
1bc6916b 606 }
6af18d2b 607 }
bf18ef3a 608 r(im, min_x, ly, max_x - min_x + 1, coverage, ctx);
6af18d2b
AMH
609 }
610 }
bf18ef3a
TC
611
612 myfree(coverage);
613}
614
615static void
616scanline_flush_color(i_img *im, i_img_dim x, i_img_dim y, i_img_dim width, const unsigned char *cover, void *ctx) {
617 flush_color_t *fc = ctx;
618
619 i_render_color(&fc->r, x, y, width, cover, &fc->c);
6af18d2b
AMH
620}
621
bf18ef3a
TC
622static void
623scanline_flush_fill(i_img *im, i_img_dim x, i_img_dim y, i_img_dim width, const unsigned char *cover, void *ctx) {
624 flush_fill_t *ff = ctx;
625
626 i_render_fill(&ff->r, x, y, width, cover, ff->fill);
627}
628
629
40068b33
TC
630/*
631=item i_circle_out(im, x, y, r, col)
632
633=category Drawing
634=synopsis i_circle_out(im, 50, 50, 45, &color);
635
636Draw a circle outline centered at (x,y) with radius r,
637non-anti-aliased.
638
639Parameters:
640
641=over
642
643=item *
644
645(x, y) - the center of the circle
646
647=item *
648
649r - the radius of the circle in pixels, must be non-negative
650
651=back
652
653Returns non-zero on success.
654
655Implementation:
656
657=cut
658*/
659
660int
661i_circle_out(i_img *im, i_img_dim xc, i_img_dim yc, i_img_dim r,
662 const i_color *col) {
663 i_img_dim x, y;
664 i_img_dim dx, dy;
665 int error;
af22c916 666 dIMCTXim(im);
40068b33 667
857e686a
TC
668 im_log((aIMCTX, 1, "i_circle_out(im %p, centre(" i_DFp "), rad %" i_DF ", col %p)\n",
669 im, i_DFcp(xc, yc), i_DFc(r), col));
670
af22c916 671 im_clear_error(aIMCTX);
40068b33
TC
672
673 if (r < 0) {
af22c916 674 im_push_error(aIMCTX, 0, "circle: radius must be non-negative");
40068b33
TC
675 return 0;
676 }
677
678 i_ppix(im, xc+r, yc, col);
679 i_ppix(im, xc-r, yc, col);
680 i_ppix(im, xc, yc+r, col);
681 i_ppix(im, xc, yc-r, col);
682
683 x = 0;
684 y = r;
685 dx = 1;
686 dy = -2 * r;
687 error = 1 - r;
688 while (x < y) {
689 if (error >= 0) {
690 --y;
691 dy += 2;
692 error += dy;
693 }
694 ++x;
695 dx += 2;
696 error += dx;
697
698 i_ppix(im, xc + x, yc + y, col);
699 i_ppix(im, xc + x, yc - y, col);
700 i_ppix(im, xc - x, yc + y, col);
701 i_ppix(im, xc - x, yc - y, col);
702 if (x != y) {
703 i_ppix(im, xc + y, yc + x, col);
704 i_ppix(im, xc + y, yc - x, col);
705 i_ppix(im, xc - y, yc + x, col);
706 i_ppix(im, xc - y, yc - x, col);
707 }
708 }
709
710 return 1;
711}
712
713/*
714=item arc_seg(angle)
715
716Convert an angle in degrees into an angle measure we can generate
717simply from the numbers we have when drawing the circle.
718
12db268a 719=cut
40068b33
TC
720*/
721
722static i_img_dim
723arc_seg(double angle, int scale) {
724 i_img_dim seg = (angle + 45) / 90;
725 double remains = angle - seg * 90; /* should be in the range [-45,45] */
40068b33
TC
726
727 while (seg > 4)
728 seg -= 4;
729 if (seg == 4 && remains > 0)
730 seg = 0;
731
732 return scale * (seg * 2 + sin(remains * PI/180));
733}
734
735/*
736=item i_arc_out(im, x, y, r, d1, d2, col)
737
738=category Drawing
739=synopsis i_arc_out(im, 50, 50, 45, 45, 135, &color);
740
741Draw an arc outline centered at (x,y) with radius r, non-anti-aliased
742over the angle range d1 through d2 degrees.
743
744Parameters:
745
746=over
747
748=item *
749
750(x, y) - the center of the circle
751
752=item *
753
754r - the radius of the circle in pixels, must be non-negative
755
756=item *
757
758d1, d2 - the range of angles to draw the arc over, in degrees.
759
760=back
761
762Returns non-zero on success.
763
764Implementation:
765
766=cut
767*/
768
769int
770i_arc_out(i_img *im, i_img_dim xc, i_img_dim yc, i_img_dim r,
8d14daab 771 double d1, double d2, const i_color *col) {
40068b33
TC
772 i_img_dim x, y;
773 i_img_dim dx, dy;
774 int error;
775 i_img_dim segs[2][2];
776 int seg_count;
777 i_img_dim sin_th;
778 i_img_dim seg_d1, seg_d2;
779 int seg_num;
40068b33
TC
780 i_img_dim scale = r + 1;
781 i_img_dim seg1 = scale * 2;
782 i_img_dim seg2 = scale * 4;
783 i_img_dim seg3 = scale * 6;
784 i_img_dim seg4 = scale * 8;
857e686a
TC
785 dIMCTXim(im);
786
787 im_log((aIMCTX,1,"i_arc_out(im %p,centre(" i_DFp "), rad %" i_DF ", d1 %f, d2 %f, col %p)",
788 im, i_DFcp(xc, yc), i_DFc(r), d1, d2, col));
40068b33 789
af22c916 790 im_clear_error(aIMCTX);
40068b33
TC
791
792 if (r <= 0) {
af22c916 793 im_push_error(aIMCTX, 0, "arc: radius must be non-negative");
40068b33
TC
794 return 0;
795 }
796 if (d1 + 360 <= d2)
797 return i_circle_out(im, xc, yc, r, col);
798
799 if (d1 < 0)
800 d1 += 360 * floor((-d1 + 359) / 360);
801 if (d2 < 0)
802 d2 += 360 * floor((-d2 + 359) / 360);
803 d1 = fmod(d1, 360);
804 d2 = fmod(d2, 360);
805 seg_d1 = arc_seg(d1, scale);
806 seg_d2 = arc_seg(d2, scale);
807 if (seg_d2 < seg_d1) {
808 /* split into two segments */
809 segs[0][0] = 0;
810 segs[0][1] = seg_d2;
811 segs[1][0] = seg_d1;
812 segs[1][1] = seg4;
813 seg_count = 2;
814 }
815 else {
816 segs[0][0] = seg_d1;
817 segs[0][1] = seg_d2;
818 seg_count = 1;
819 }
820
821 for (seg_num = 0; seg_num < seg_count; ++seg_num) {
822 i_img_dim seg_start = segs[seg_num][0];
823 i_img_dim seg_end = segs[seg_num][1];
824 if (seg_start == 0)
825 i_ppix(im, xc+r, yc, col);
826 if (seg_start <= seg1 && seg_end >= seg1)
827 i_ppix(im, xc, yc+r, col);
828 if (seg_start <= seg2 && seg_end >= seg2)
829 i_ppix(im, xc-r, yc, col);
830 if (seg_start <= seg3 && seg_end >= seg3)
831 i_ppix(im, xc, yc-r, col);
832
833 y = 0;
834 x = r;
835 dy = 1;
836 dx = -2 * r;
837 error = 1 - r;
838 while (y < x) {
839 if (error >= 0) {
840 --x;
841 dx += 2;
842 error += dx;
843 }
844 ++y;
845 dy += 2;
846 error += dy;
847
848 sin_th = y;
849 if (seg_start <= sin_th && seg_end >= sin_th)
850 i_ppix(im, xc + x, yc + y, col);
851 if (seg_start <= seg1 - sin_th && seg_end >= seg1 - sin_th)
852 i_ppix(im, xc + y, yc + x, col);
853
854 if (seg_start <= seg1 + sin_th && seg_end >= seg1 + sin_th)
855 i_ppix(im, xc - y, yc + x, col);
856 if (seg_start <= seg2 - sin_th && seg_end >= seg2 - sin_th)
857 i_ppix(im, xc - x, yc + y, col);
858
859 if (seg_start <= seg2 + sin_th && seg_end >= seg2 + sin_th)
860 i_ppix(im, xc - x, yc - y, col);
861 if (seg_start <= seg3 - sin_th && seg_end >= seg3 - sin_th)
862 i_ppix(im, xc - y, yc - x, col);
863
864 if (seg_start <= seg3 + sin_th && seg_end >= seg3 + sin_th)
865 i_ppix(im, xc + y, yc - x, col);
866 if (seg_start <= seg4 - sin_th && seg_end >= seg4 - sin_th)
867 i_ppix(im, xc + x, yc - y, col);
868 }
869 }
870
871 return 1;
872}
873
874static double
875cover(i_img_dim r, i_img_dim j) {
8d14daab 876 double rjsqrt = sqrt(r*r - j*j);
40068b33
TC
877
878 return ceil(rjsqrt) - rjsqrt;
879}
880
881/*
882=item i_circle_out_aa(im, xc, yc, r, col)
883
884=synopsis i_circle_out_aa(im, 50, 50, 45, &color);
885
886Draw a circle outline centered at (x,y) with radius r, anti-aliased.
887
888Parameters:
889
890=over
891
892=item *
893
894(xc, yc) - the center of the circle
895
896=item *
897
898r - the radius of the circle in pixels, must be non-negative
899
900=item *
901
902col - an i_color for the color to draw in.
903
904=back
905
906Returns non-zero on success.
907
908=cut
909
910Based on "Fast Anti-Aliased Circle Generation", Xiaolin Wu, Graphics
911Gems.
912
913I use floating point for I<D> since for large circles the precision of
914a [0,255] value isn't sufficient when approaching the end of the
915octant.
916
917*/
918
919int
920i_circle_out_aa(i_img *im, i_img_dim xc, i_img_dim yc, i_img_dim r, const i_color *col) {
921 i_img_dim i, j;
922 double t;
923 i_color workc = *col;
924 int orig_alpha = col->channel[3];
af22c916 925 dIMCTXim(im);
dc95a369 926
857e686a
TC
927 im_log((aIMCTX,1,"i_circle_out_aa(im %p,centre(" i_DFp "), rad %" i_DF ", col %p)",
928 im, i_DFcp(xc, yc), i_DFc(r), col));
929
af22c916 930 im_clear_error(aIMCTX);
40068b33 931 if (r <= 0) {
af22c916 932 im_push_error(aIMCTX, 0, "arc: radius must be non-negative");
40068b33
TC
933 return 0;
934 }
935 i = r;
936 j = 0;
937 t = 0;
938 i_ppix_norm(im, xc+i, yc+j, col);
939 i_ppix_norm(im, xc-i, yc+j, col);
940 i_ppix_norm(im, xc+j, yc+i, col);
941 i_ppix_norm(im, xc+j, yc-i, col);
942
943 while (i > j+1) {
944 double d;
945 int cv, inv_cv;
40068b33
TC
946 j++;
947 d = cover(r, j);
948 cv = (int)(d * 255 + 0.5);
949 inv_cv = 255-cv;
950 if (d < t) {
951 --i;
952 }
953 if (inv_cv) {
954 workc.channel[3] = orig_alpha * inv_cv / 255;
955 i_ppix_norm(im, xc+i, yc+j, &workc);
956 i_ppix_norm(im, xc-i, yc+j, &workc);
957 i_ppix_norm(im, xc+i, yc-j, &workc);
958 i_ppix_norm(im, xc-i, yc-j, &workc);
959
960 if (i != j) {
961 i_ppix_norm(im, xc+j, yc+i, &workc);
962 i_ppix_norm(im, xc-j, yc+i, &workc);
963 i_ppix_norm(im, xc+j, yc-i, &workc);
964 i_ppix_norm(im, xc-j, yc-i, &workc);
965 }
966 }
967 if (cv && i > j) {
968 workc.channel[3] = orig_alpha * cv / 255;
969 i_ppix_norm(im, xc+i-1, yc+j, &workc);
970 i_ppix_norm(im, xc-i+1, yc+j, &workc);
971 i_ppix_norm(im, xc+i-1, yc-j, &workc);
972 i_ppix_norm(im, xc-i+1, yc-j, &workc);
973
974 if (j != i-1) {
975 i_ppix_norm(im, xc+j, yc+i-1, &workc);
976 i_ppix_norm(im, xc-j, yc+i-1, &workc);
977 i_ppix_norm(im, xc+j, yc-i+1, &workc);
978 i_ppix_norm(im, xc-j, yc-i+1, &workc);
979 }
980 }
981 t = d;
982 }
983
984 return 1;
985}
986
987/*
988=item i_arc_out_aa(im, xc, yc, r, d1, d2, col)
989
990=synopsis i_arc_out_aa(im, 50, 50, 45, 45, 125, &color);
991
992Draw a circle arc outline centered at (x,y) with radius r, from angle
993d1 degrees through angle d2 degrees, anti-aliased.
994
995Parameters:
996
997=over
998
999=item *
1000
1001(xc, yc) - the center of the circle
1002
1003=item *
1004
1005r - the radius of the circle in pixels, must be non-negative
1006
1007=item *
1008
1009d1, d2 - the range of angle in degrees to draw the arc through. If
1010d2-d1 >= 360 a full circle is drawn.
1011
1012=back
1013
1014Returns non-zero on success.
1015
1016=cut
1017
1018Based on "Fast Anti-Aliased Circle Generation", Xiaolin Wu, Graphics
1019Gems.
1020
1021*/
1022
1023int
8d14daab 1024i_arc_out_aa(i_img *im, i_img_dim xc, i_img_dim yc, i_img_dim r, double d1, double d2, const i_color *col) {
40068b33
TC
1025 i_img_dim i, j;
1026 double t;
1027 i_color workc = *col;
1028 i_img_dim segs[2][2];
1029 int seg_count;
1030 i_img_dim sin_th;
1031 i_img_dim seg_d1, seg_d2;
1032 int seg_num;
1033 int orig_alpha = col->channel[3];
1034 i_img_dim scale = r + 1;
1035 i_img_dim seg1 = scale * 2;
1036 i_img_dim seg2 = scale * 4;
1037 i_img_dim seg3 = scale * 6;
1038 i_img_dim seg4 = scale * 8;
af22c916 1039 dIMCTXim(im);
40068b33 1040
857e686a
TC
1041 im_log((aIMCTX,1,"i_arc_out_aa(im %p,centre(" i_DFp "), rad %" i_DF ", d1 %f, d2 %f, col %p)",
1042 im, i_DFcp(xc, yc), i_DFc(r), d1, d2, col));
1043
af22c916 1044 im_clear_error(aIMCTX);
40068b33 1045 if (r <= 0) {
af22c916 1046 im_push_error(aIMCTX, 0, "arc: radius must be non-negative");
40068b33
TC
1047 return 0;
1048 }
1049 if (d1 + 360 <= d2)
1050 return i_circle_out_aa(im, xc, yc, r, col);
1051
1052 if (d1 < 0)
1053 d1 += 360 * floor((-d1 + 359) / 360);
1054 if (d2 < 0)
1055 d2 += 360 * floor((-d2 + 359) / 360);
1056 d1 = fmod(d1, 360);
1057 d2 = fmod(d2, 360);
1058 seg_d1 = arc_seg(d1, scale);
1059 seg_d2 = arc_seg(d2, scale);
1060 if (seg_d2 < seg_d1) {
1061 /* split into two segments */
1062 segs[0][0] = 0;
1063 segs[0][1] = seg_d2;
1064 segs[1][0] = seg_d1;
1065 segs[1][1] = seg4;
1066 seg_count = 2;
1067 }
1068 else {
1069 segs[0][0] = seg_d1;
1070 segs[0][1] = seg_d2;
1071 seg_count = 1;
1072 }
1073
1074 for (seg_num = 0; seg_num < seg_count; ++seg_num) {
1075 i_img_dim seg_start = segs[seg_num][0];
1076 i_img_dim seg_end = segs[seg_num][1];
1077
1078 i = r;
1079 j = 0;
1080 t = 0;
1081
1082 if (seg_start == 0)
1083 i_ppix_norm(im, xc+i, yc+j, col);
1084 if (seg_start <= seg1 && seg_end >= seg1)
1085 i_ppix_norm(im, xc+j, yc+i, col);
1086 if (seg_start <= seg2 && seg_end >= seg2)
1087 i_ppix_norm(im, xc-i, yc+j, col);
1088 if (seg_start <= seg3 && seg_end >= seg3)
1089 i_ppix_norm(im, xc+j, yc-i, col);
1090
1091 while (i > j+1) {
1092 int cv, inv_cv;
40068b33
TC
1093 double d;
1094 j++;
1095 d = cover(r, j);
1096 cv = (int)(d * 255 + 0.5);
1097 inv_cv = 255-cv;
1098 if (d < t) {
1099 --i;
1100 }
1101 sin_th = j;
1102 if (inv_cv) {
1103 workc.channel[3] = orig_alpha * inv_cv / 255;
1104
1105 if (seg_start <= sin_th && seg_end >= sin_th)
1106 i_ppix_norm(im, xc+i, yc+j, &workc);
1107 if (seg_start <= seg2 - sin_th && seg_end >= seg2 - sin_th)
1108 i_ppix_norm(im, xc-i, yc+j, &workc);
1109 if (seg_start <= seg4 - sin_th && seg_end >= seg4 - sin_th)
1110 i_ppix_norm(im, xc+i, yc-j, &workc);
1111 if (seg_start <= seg2 + sin_th && seg_end >= seg2 + sin_th)
1112 i_ppix_norm(im, xc-i, yc-j, &workc);
1113
1114 if (i != j) {
1115 if (seg_start <= seg1 - sin_th && seg_end >= seg1 - sin_th)
1116 i_ppix_norm(im, xc+j, yc+i, &workc);
1117 if (seg_start <= seg1 + sin_th && seg_end >= seg1 + sin_th)
1118 i_ppix_norm(im, xc-j, yc+i, &workc);
1119 if (seg_start <= seg3 + sin_th && seg_end >= seg3 + sin_th)
1120 i_ppix_norm(im, xc+j, yc-i, &workc);
1121 if (seg_start <= seg3 - sin_th && seg_end >= seg3 - sin_th)
1122 i_ppix_norm(im, xc-j, yc-i, &workc);
1123 }
1124 }
1125 if (cv && i > j) {
1126 workc.channel[3] = orig_alpha * cv / 255;
1127 if (seg_start <= sin_th && seg_end >= sin_th)
1128 i_ppix_norm(im, xc+i-1, yc+j, &workc);
1129 if (seg_start <= seg2 - sin_th && seg_end >= seg2 - sin_th)
1130 i_ppix_norm(im, xc-i+1, yc+j, &workc);
1131 if (seg_start <= seg4 - sin_th && seg_end >= seg4 - sin_th)
1132 i_ppix_norm(im, xc+i-1, yc-j, &workc);
1133 if (seg_start <= seg2 + sin_th && seg_end >= seg2 + sin_th)
1134 i_ppix_norm(im, xc-i+1, yc-j, &workc);
1135
1136 if (seg_start <= seg1 - sin_th && seg_end >= seg1 - sin_th)
1137 i_ppix_norm(im, xc+j, yc+i-1, &workc);
1138 if (seg_start <= seg1 + sin_th && seg_end >= seg1 + sin_th)
1139 i_ppix_norm(im, xc-j, yc+i-1, &workc);
1140 if (seg_start <= seg3 + sin_th && seg_end >= seg3 + sin_th)
1141 i_ppix_norm(im, xc+j, yc-i+1, &workc);
1142 if (seg_start <= seg3 - sin_th && seg_end >= seg3 - sin_th)
1143 i_ppix_norm(im, xc-j, yc-i+1, &workc);
1144 }
1145 t = d;
1146 }
1147 }
1148
1149 return 1;
1150}
1151
92bda632
TC
1152/*
1153=item i_box(im, x1, y1, x2, y2, color)
6af18d2b 1154
92bda632
TC
1155=category Drawing
1156=synopsis i_box(im, 0, 0, im->xsize-1, im->ysize-1, &color).
6af18d2b 1157
92bda632 1158Outlines the box from (x1,y1) to (x2,y2) inclusive with I<color>.
6af18d2b 1159
92bda632
TC
1160=cut
1161*/
6af18d2b 1162
02d1d628 1163void
8d14daab
TC
1164i_box(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,const i_color *val) {
1165 i_img_dim x,y;
857e686a
TC
1166 dIMCTXim(im);
1167
1168 im_log((aIMCTX, 1,"i_box(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n",
8d14daab 1169 im, i_DFcp(x1,y1), i_DFcp(x2,y2), val));
02d1d628
AMH
1170 for(x=x1;x<x2+1;x++) {
1171 i_ppix(im,x,y1,val);
1172 i_ppix(im,x,y2,val);
1173 }
1174 for(y=y1;y<y2+1;y++) {
1175 i_ppix(im,x1,y,val);
1176 i_ppix(im,x2,y,val);
1177 }
1178}
1179
92bda632
TC
1180/*
1181=item i_box_filled(im, x1, y1, x2, y2, color)
1182
1183=category Drawing
1184=synopsis i_box_filled(im, 0, 0, im->xsize-1, im->ysize-1, &color);
1185
1186Fills the box from (x1,y1) to (x2,y2) inclusive with color.
1187
1188=cut
1189*/
1190
02d1d628 1191void
8d14daab 1192i_box_filled(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2, const i_color *val) {
3b000586
TC
1193 i_img_dim x, y, width;
1194 i_palidx index;
857e686a 1195 dIMCTXim(im);
3b000586 1196
857e686a 1197 im_log((aIMCTX,1,"i_box_filled(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n",
8d14daab 1198 im, i_DFcp(x1, y1), i_DFcp(x2,y2) ,val));
3b000586
TC
1199
1200 if (x1 > x2 || y1 > y2
1201 || x2 < 0 || y2 < 0
1202 || x1 >= im->xsize || y1 > im->ysize)
1203 return;
1204
1205 if (x1 < 0)
1206 x1 = 0;
1207 if (x2 >= im->xsize)
1208 x2 = im->xsize - 1;
1209 if (y1 < 0)
1210 y1 = 0;
1211 if (y2 >= im->ysize)
1212 y2 = im->ysize - 1;
1213
1214 width = x2 - x1 + 1;
1215
1216 if (im->type == i_palette_type
1217 && i_findcolor(im, val, &index)) {
1218 i_palidx *line = mymalloc(sizeof(i_palidx) * width);
1219
1220 for (x = 0; x < width; ++x)
1221 line[x] = index;
1222
1223 for (y = y1; y <= y2; ++y)
1224 i_ppal(im, x1, x2+1, y, line);
1225
1226 myfree(line);
1227 }
1228 else {
1229 i_color *line = mymalloc(sizeof(i_color) * width);
1230
1231 for (x = 0; x < width; ++x)
1232 line[x] = *val;
1233
1234 for (y = y1; y <= y2; ++y)
1235 i_plin(im, x1, x2+1, y, line);
1236
1237 myfree(line);
1238 }
02d1d628
AMH
1239}
1240
7477ff14
TC
1241/*
1242=item i_box_filledf(im, x1, y1, x2, y2, color)
1243
1244=category Drawing
1245=synopsis i_box_filledf(im, 0, 0, im->xsize-1, im->ysize-1, &fcolor);
1246
1247Fills the box from (x1,y1) to (x2,y2) inclusive with a floating point
1248color.
1249
1250=cut
1251*/
1252
1253int
8d14daab 1254i_box_filledf(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2, const i_fcolor *val) {
7477ff14 1255 i_img_dim x, y, width;
857e686a 1256 dIMCTXim(im);
7477ff14 1257
857e686a 1258 im_log((aIMCTX, 1,"i_box_filledf(im* %p, p1(" i_DFp "), p2(" i_DFp "),val %p)\n",
8d14daab 1259 im, i_DFcp(x1, y1), i_DFcp(x2, y2), val));
7477ff14
TC
1260
1261 if (x1 > x2 || y1 > y2
1262 || x2 < 0 || y2 < 0
1263 || x1 >= im->xsize || y1 > im->ysize)
1264 return 0;
1265
1266 if (x1 < 0)
1267 x1 = 0;
1268 if (x2 >= im->xsize)
1269 x2 = im->xsize - 1;
1270 if (y1 < 0)
1271 y1 = 0;
1272 if (y2 >= im->ysize)
1273 y2 = im->ysize - 1;
1274
1275 width = x2 - x1 + 1;
1276
1277 if (im->bits <= 8) {
1278 i_color c;
1279 c.rgba.r = SampleFTo8(val->rgba.r);
1280 c.rgba.g = SampleFTo8(val->rgba.g);
1281 c.rgba.b = SampleFTo8(val->rgba.b);
1282 c.rgba.a = SampleFTo8(val->rgba.a);
1283
1284 i_box_filled(im, x1, y1, x2, y2, &c);
1285 }
1286 else {
1287 i_fcolor *line = mymalloc(sizeof(i_fcolor) * width);
1288
1289 for (x = 0; x < width; ++x)
1290 line[x] = *val;
1291
1292 for (y = y1; y <= y2; ++y)
1293 i_plinf(im, x1, x2+1, y, line);
1294
1295 myfree(line);
1296 }
1297
1298 return 1;
1299}
1300
92bda632
TC
1301/*
1302=item i_box_cfill(im, x1, y1, x2, y2, fill)
1303
1304=category Drawing
1305=synopsis i_box_cfill(im, 0, 0, im->xsize-1, im->ysize-1, fill);
1306
1307Fills the box from (x1,y1) to (x2,y2) inclusive with fill.
1308
1309=cut
1310*/
1311
f1ac5027 1312void
8d14daab 1313i_box_cfill(i_img *im,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_fill_t *fill) {
9b1ec2b8 1314 i_render r;
857e686a 1315 dIMCTXim(im);
8d14daab 1316
857e686a 1317 im_log((aIMCTX,1,"i_box_cfill(im* %p, p1(" i_DFp "), p2(" i_DFp "), fill %p)\n",
8d14daab 1318 im, i_DFcp(x1, y1), i_DFcp(x2,y2), fill));
f1ac5027
TC
1319
1320 ++x2;
f0960b14
TC
1321 if (x1 < 0)
1322 x1 = 0;
1323 if (y1 < 0)
1324 y1 = 0;
1325 if (x2 > im->xsize)
1326 x2 = im->xsize;
1327 if (y2 >= im->ysize)
1328 y2 = im->ysize-1;
1329 if (x1 >= x2 || y1 > y2)
1330 return;
9b1ec2b8
TC
1331
1332 i_render_init(&r, im, x2-x1);
1333 while (y1 <= y2) {
1334 i_render_fill(&r, x1, y1, x2-x1, NULL, fill);
1335 ++y1;
f1ac5027 1336 }
9b1ec2b8 1337 i_render_done(&r);
f1ac5027 1338}
02d1d628 1339
aa833c97 1340/*
5715f7c3 1341=item i_line(C<im>, C<x1>, C<y1>, C<x2>, C<y2>, C<color>, C<endp>)
aa833c97 1342
92bda632
TC
1343=category Drawing
1344
5715f7c3 1345=for stopwords Bresenham's
aa833c97 1346
5715f7c3
TC
1347Draw a line to image using Bresenham's line drawing algorithm
1348
1349 im - image to draw to
1350 x1 - starting x coordinate
1351 y1 - starting x coordinate
1352 x2 - starting x coordinate
1353 y2 - starting x coordinate
1354 color - color to write to image
1355 endp - endpoint flag (boolean)
aa833c97
AMH
1356
1357=cut
1358*/
1359
02d1d628 1360void
8d14daab
TC
1361i_line(i_img *im, i_img_dim x1, i_img_dim y1, i_img_dim x2, i_img_dim y2, const i_color *val, int endp) {
1362 i_img_dim x, y;
1363 i_img_dim dx, dy;
1364 i_img_dim p;
02d1d628 1365
aa833c97
AMH
1366 dx = x2 - x1;
1367 dy = y2 - y1;
02d1d628 1368
aa833c97
AMH
1369
1370 /* choose variable to iterate on */
8d14daab
TC
1371 if (i_abs(dx) > i_abs(dy)) {
1372 i_img_dim dx2, dy2, cpy;
aa833c97
AMH
1373
1374 /* sort by x */
1375 if (x1 > x2) {
8d14daab 1376 i_img_dim t;
aa833c97
AMH
1377 t = x1; x1 = x2; x2 = t;
1378 t = y1; y1 = y2; y2 = t;
02d1d628 1379 }
aa833c97 1380
8d14daab 1381 dx = i_abs(dx);
aa833c97
AMH
1382 dx2 = dx*2;
1383 dy = y2 - y1;
1384
1385 if (dy<0) {
1386 dy = -dy;
1387 cpy = -1;
1388 } else {
1389 cpy = 1;
1390 }
1391 dy2 = dy*2;
1392 p = dy2 - dx;
1393
1394
1395 y = y1;
1396 for(x=x1; x<x2-1; x++) {
1397 if (p<0) {
1398 p += dy2;
1399 } else {
1400 y += cpy;
1401 p += dy2-dx2;
1402 }
1403 i_ppix(im, x+1, y, val);
02d1d628 1404 }
aa833c97 1405 } else {
8d14daab 1406 i_img_dim dy2, dx2, cpx;
aa833c97
AMH
1407
1408 /* sort bx y */
1409 if (y1 > y2) {
8d14daab 1410 i_img_dim t;
aa833c97
AMH
1411 t = x1; x1 = x2; x2 = t;
1412 t = y1; y1 = y2; y2 = t;
1413 }
1414
8d14daab 1415 dy = i_abs(dy);
aa833c97
AMH
1416 dx = x2 - x1;
1417 dy2 = dy*2;
1418
1419 if (dx<0) {
1420 dx = -dx;
1421 cpx = -1;
1422 } else {
1423 cpx = 1;
1424 }
1425 dx2 = dx*2;
1426 p = dx2 - dy;
1427
1428 x = x1;
1429
1430 for(y=y1; y<y2-1; y++) {
1431 if (p<0) {
1432 p += dx2;
1433 } else {
1434 x += cpx;
1435 p += dx2-dy2;
1436 }
1437 i_ppix(im, x, y+1, val);
1438 }
1439 }
1440 if (endp) {
1441 i_ppix(im, x1, y1, val);
1442 i_ppix(im, x2, y2, val);
1443 } else {
1444 if (x1 != x2 || y1 != y2)
1445 i_ppix(im, x1, y1, val);
1446 }
02d1d628
AMH
1447}
1448
aa833c97 1449
02d1d628 1450void
8d14daab 1451i_line_dda(i_img *im, i_img_dim x1, i_img_dim y1, i_img_dim x2, i_img_dim y2, i_color *val) {
b437ce0a 1452
8d14daab
TC
1453 double dy;
1454 i_img_dim x;
b437ce0a
AMH
1455
1456 for(x=x1; x<=x2; x++) {
8d14daab
TC
1457 dy = y1+ (x-x1)/(double)(x2-x1)*(y2-y1);
1458 i_ppix(im, x, (i_img_dim)(dy+0.5), val);
b437ce0a
AMH
1459 }
1460}
1461
92bda632 1462/*
5715f7c3 1463=item i_line_aa(C<im>, C<x1>, C<x2>, C<y1>, C<y2>, C<color>, C<endp>)
92bda632
TC
1464
1465=category Drawing
1466
5715f7c3 1467Anti-alias draws a line from (x1,y1) to (x2, y2) in color.
b437ce0a 1468
5715f7c3 1469The point (x2, y2) is drawn only if C<endp> is set.
92bda632
TC
1470
1471=cut
1472*/
b437ce0a
AMH
1473
1474void
8d14daab
TC
1475i_line_aa(i_img *im, i_img_dim x1, i_img_dim y1, i_img_dim x2, i_img_dim y2, const i_color *val, int endp) {
1476 i_img_dim x, y;
1477 i_img_dim dx, dy;
1478 i_img_dim p;
b437ce0a
AMH
1479
1480 dx = x2 - x1;
1481 dy = y2 - y1;
1482
1483 /* choose variable to iterate on */
8d14daab
TC
1484 if (i_abs(dx) > i_abs(dy)) {
1485 i_img_dim dx2, dy2, cpy;
b437ce0a
AMH
1486
1487 /* sort by x */
1488 if (x1 > x2) {
8d14daab 1489 i_img_dim t;
b437ce0a
AMH
1490 t = x1; x1 = x2; x2 = t;
1491 t = y1; y1 = y2; y2 = t;
1492 }
1493
8d14daab 1494 dx = i_abs(dx);
b437ce0a
AMH
1495 dx2 = dx*2;
1496 dy = y2 - y1;
1497
1498 if (dy<0) {
1499 dy = -dy;
1500 cpy = -1;
1501 } else {
1502 cpy = 1;
1503 }
1504 dy2 = dy*2;
1505 p = dy2 - dx2; /* this has to be like this for AA */
1506
1507 y = y1;
1508
1509 for(x=x1; x<x2-1; x++) {
1510 int ch;
1511 i_color tval;
8d14daab
TC
1512 double t = (dy) ? -(float)(p)/(float)(dx2) : 1;
1513 double t1, t2;
b437ce0a
AMH
1514
1515 if (t<0) t = 0;
1516 t1 = 1-t;
1517 t2 = t;
1518
1519 i_gpix(im,x+1,y,&tval);
1520 for(ch=0;ch<im->channels;ch++)
1521 tval.channel[ch]=(unsigned char)(t1*(float)tval.channel[ch]+t2*(float)val->channel[ch]);
1522 i_ppix(im,x+1,y,&tval);
1523
1524 i_gpix(im,x+1,y+cpy,&tval);
1525 for(ch=0;ch<im->channels;ch++)
1526 tval.channel[ch]=(unsigned char)(t2*(float)tval.channel[ch]+t1*(float)val->channel[ch]);
1527 i_ppix(im,x+1,y+cpy,&tval);
1528
1529 if (p<0) {
1530 p += dy2;
1531 } else {
1532 y += cpy;
1533 p += dy2-dx2;
1534 }
1535 }
1536 } else {
8d14daab 1537 i_img_dim dy2, dx2, cpx;
b437ce0a
AMH
1538
1539 /* sort bx y */
1540 if (y1 > y2) {
8d14daab 1541 i_img_dim t;
b437ce0a
AMH
1542 t = x1; x1 = x2; x2 = t;
1543 t = y1; y1 = y2; y2 = t;
1544 }
1545
8d14daab 1546 dy = i_abs(dy);
b437ce0a
AMH
1547 dx = x2 - x1;
1548 dy2 = dy*2;
1549
1550 if (dx<0) {
1551 dx = -dx;
1552 cpx = -1;
1553 } else {
1554 cpx = 1;
1555 }
1556 dx2 = dx*2;
1557 p = dx2 - dy2; /* this has to be like this for AA */
1558
1559 x = x1;
1560
1561 for(y=y1; y<y2-1; y++) {
1562 int ch;
1563 i_color tval;
8d14daab
TC
1564 double t = (dx) ? -(double)(p)/(double)(dy2) : 1;
1565 double t1, t2;
b437ce0a
AMH
1566
1567 if (t<0) t = 0;
1568 t1 = 1-t;
1569 t2 = t;
1570
1571 i_gpix(im,x,y+1,&tval);
1572 for(ch=0;ch<im->channels;ch++)
8d14daab 1573 tval.channel[ch]=(unsigned char)(t1*(double)tval.channel[ch]+t2*(double)val->channel[ch]);
b437ce0a
AMH
1574 i_ppix(im,x,y+1,&tval);
1575
1576 i_gpix(im,x+cpx,y+1,&tval);
1577 for(ch=0;ch<im->channels;ch++)
8d14daab 1578 tval.channel[ch]=(unsigned char)(t2*(double)tval.channel[ch]+t1*(double)val->channel[ch]);
b437ce0a
AMH
1579 i_ppix(im,x+cpx,y+1,&tval);
1580
1581 if (p<0) {
1582 p += dx2;
1583 } else {
1584 x += cpx;
1585 p += dx2-dy2;
1586 }
1587 }
1588 }
1589
1590
1591 if (endp) {
1592 i_ppix(im, x1, y1, val);
1593 i_ppix(im, x2, y2, val);
1594 } else {
1595 if (x1 != x2 || y1 != y2)
1596 i_ppix(im, x1, y1, val);
1597 }
1598}
1599
1600
1601
b33c08f8 1602static double
8d14daab 1603perm(i_img_dim n,i_img_dim k) {
02d1d628 1604 double r;
8d14daab 1605 i_img_dim i;
02d1d628
AMH
1606 r=1;
1607 for(i=k+1;i<=n;i++) r*=i;
1608 for(i=1;i<=(n-k);i++) r/=i;
1609 return r;
1610}
1611
1612
1613/* Note in calculating t^k*(1-t)^(n-k)
1614 we can start by using t^0=1 so this simplifies to
1615 t^0*(1-t)^n - we want to multiply that with t/(1-t) each iteration
1616 to get a new level - this may lead to errors who knows lets test it */
1617
1618void
97ac0a96 1619i_bezier_multi(i_img *im,int l,const double *x,const double *y, const i_color *val) {
02d1d628
AMH
1620 double *bzcoef;
1621 double t,cx,cy;
1622 int k,i;
8d14daab 1623 i_img_dim lx = 0,ly = 0;
02d1d628
AMH
1624 int n=l-1;
1625 double itr,ccoef;
1626
f0960b14
TC
1627 /* this is the same size as the x and y arrays, so shouldn't overflow */
1628 bzcoef=mymalloc(sizeof(double)*l); /* checked 5jul05 tonyc */
02d1d628
AMH
1629 for(k=0;k<l;k++) bzcoef[k]=perm(n,k);
1630 ICL_info(val);
1631
1632
1633 /* for(k=0;k<l;k++) printf("bzcoef: %d -> %f\n",k,bzcoef[k]); */
1634 i=0;
1635 for(t=0;t<=1;t+=0.005) {
1636 cx=cy=0;
1637 itr=t/(1-t);
1638 ccoef=pow(1-t,n);
1639 for(k=0;k<l;k++) {
1640 /* cx+=bzcoef[k]*x[k]*pow(t,k)*pow(1-t,n-k);
1641 cy+=bzcoef[k]*y[k]*pow(t,k)*pow(1-t,n-k);*/
1642
1643 cx+=bzcoef[k]*x[k]*ccoef;
1644 cy+=bzcoef[k]*y[k]*ccoef;
1645 ccoef*=itr;
1646 }
1647 /* printf("%f -> (%d,%d)\n",t,(int)(0.5+cx),(int)(0.5+cy)); */
1648 if (i++) {
8d14daab 1649 i_line_aa(im,lx,ly,(i_img_dim)(0.5+cx),(i_img_dim)(0.5+cy),val, 1);
02d1d628 1650 }
8d14daab
TC
1651 /* i_ppix(im,(i_img_dim)(0.5+cx),(i_img_dim)(0.5+cy),val); */
1652 lx=(i_img_dim)(0.5+cx);
1653 ly=(i_img_dim)(0.5+cy);
02d1d628
AMH
1654 }
1655 ICL_info(val);
1656 myfree(bzcoef);
1657}
1658
02d1d628
AMH
1659/* Flood fill
1660
1661 REF: Graphics Gems I. page 282+
1662
1663*/
1664
02d1d628
AMH
1665/* This should be moved into a seperate file? */
1666
1667/* This is the truncation used:
1668
1669 a double is multiplied by 16 and then truncated.
1670 This means that 0 -> 0
1671 So a triangle of (0,0) (10,10) (10,0) Will look like it's
1672 not filling the (10,10) point nor the (10,0)-(10,10) line segment
1673
1674*/
1675
1676
02d1d628
AMH
1677/* Flood fill algorithm - based on the Ken Fishkins (pixar) gem in
1678 graphics gems I */
1679
1680/*
1681struct stc {
8d14daab
TC
1682 i_img_dim mylx,myrx;
1683 i_img_dim dadlx,dadrx;
1684 i_img_dim myy;
02d1d628
AMH
1685 int mydirection;
1686};
1687
1688Not used code???
1689*/
1690
1691
1692struct stack_element {
8d14daab
TC
1693 i_img_dim myLx,myRx;
1694 i_img_dim dadLx,dadRx;
1695 i_img_dim myY;
02d1d628
AMH
1696 int myDirection;
1697};
1698
1699
1700/* create the link data to put push onto the stack */
1701
1702static
1703struct stack_element*
8d14daab 1704crdata(i_img_dim left,i_img_dim right,i_img_dim dadl,i_img_dim dadr,i_img_dim y, int dir) {
02d1d628 1705 struct stack_element *ste;
f0960b14 1706 ste = mymalloc(sizeof(struct stack_element)); /* checked 5jul05 tonyc */
a73aeb5f
AMH
1707 ste->myLx = left;
1708 ste->myRx = right;
1709 ste->dadLx = dadl;
1710 ste->dadRx = dadr;
1711 ste->myY = y;
1712 ste->myDirection = dir;
02d1d628
AMH
1713 return ste;
1714}
1715
1716/* i_ccomp compares two colors and gives true if they are the same */
1717
3efb0915
TC
1718typedef int (*ff_cmpfunc)(i_color const *c1, i_color const *c2, int channels);
1719
02d1d628 1720static int
3efb0915 1721i_ccomp_normal(i_color const *val1, i_color const *val2, int ch) {
02d1d628 1722 int i;
3efb0915
TC
1723 for(i = 0; i < ch; i++)
1724 if (val1->channel[i] !=val2->channel[i])
1725 return 0;
02d1d628
AMH
1726 return 1;
1727}
1728
3efb0915
TC
1729static int
1730i_ccomp_border(i_color const *val1, i_color const *val2, int ch) {
1731 int i;
1732 for(i = 0; i < ch; i++)
1733 if (val1->channel[i] !=val2->channel[i])
1734 return 1;
1735 return 0;
1736}
02d1d628
AMH
1737
1738static int
8d14daab 1739i_lspan(i_img *im, i_img_dim seedx, i_img_dim seedy, i_color const *val, ff_cmpfunc cmpfunc) {
02d1d628
AMH
1740 i_color cval;
1741 while(1) {
1742 if (seedx-1 < 0) break;
1743 i_gpix(im,seedx-1,seedy,&cval);
3efb0915
TC
1744 if (!cmpfunc(val,&cval,im->channels))
1745 break;
02d1d628
AMH
1746 seedx--;
1747 }
1748 return seedx;
1749}
1750
1751static int
8d14daab 1752i_rspan(i_img *im, i_img_dim seedx, i_img_dim seedy, i_color const *val, ff_cmpfunc cmpfunc) {
02d1d628
AMH
1753 i_color cval;
1754 while(1) {
1755 if (seedx+1 > im->xsize-1) break;
1756 i_gpix(im,seedx+1,seedy,&cval);
3efb0915 1757 if (!cmpfunc(val,&cval,im->channels)) break;
02d1d628
AMH
1758 seedx++;
1759 }
1760 return seedx;
1761}
1762
1763/* Macro to create a link and push on to the list */
1764
e25e59b1
AMH
1765#define ST_PUSH(left,right,dadl,dadr,y,dir) do { \
1766 struct stack_element *s = crdata(left,right,dadl,dadr,y,dir); \
1767 llist_push(st,&s); \
1768} while (0)
02d1d628
AMH
1769
1770/* pops the shadow on TOS into local variables lx,rx,y,direction,dadLx and dadRx */
1771/* No overflow check! */
1772
e25e59b1
AMH
1773#define ST_POP() do { \
1774 struct stack_element *s; \
1775 llist_pop(st,&s); \
1776 lx = s->myLx; \
1777 rx = s->myRx; \
1778 dadLx = s->dadLx; \
1779 dadRx = s->dadRx; \
1780 y = s->myY; \
1781 direction = s->myDirection; \
1782 myfree(s); \
1783} while (0)
1784
1785#define ST_STACK(dir,dadLx,dadRx,lx,rx,y) do { \
8d14daab
TC
1786 i_img_dim pushrx = rx+1; \
1787 i_img_dim pushlx = lx-1; \
e25e59b1
AMH
1788 ST_PUSH(lx,rx,pushlx,pushrx,y+dir,dir); \
1789 if (rx > dadRx) \
1790 ST_PUSH(dadRx+1,rx,pushlx,pushrx,y-dir,-dir); \
1791 if (lx < dadLx) ST_PUSH(lx,dadLx-1,pushlx,pushrx,y-dir,-dir); \
1792} while (0)
1793
1794#define SET(x,y) btm_set(btm,x,y)
02d1d628 1795
86d20cb9 1796/* INSIDE returns true if pixel is correct color and we haven't set it before. */
3efb0915 1797#define INSIDE(x,y, seed) ((!btm_test(btm,x,y) && ( i_gpix(im,x,y,&cval),cmpfunc(seed,&cval,channels) ) ))
02d1d628 1798
02d1d628 1799
02d1d628 1800
aa833c97
AMH
1801/* The function that does all the real work */
1802
1803static struct i_bitmap *
8d14daab
TC
1804i_flood_fill_low(i_img *im,i_img_dim seedx,i_img_dim seedy,
1805 i_img_dim *bxminp, i_img_dim *bxmaxp, i_img_dim *byminp, i_img_dim *bymaxp,
3efb0915 1806 i_color const *seed, ff_cmpfunc cmpfunc) {
8d14daab
TC
1807 i_img_dim ltx, rtx;
1808 i_img_dim tx = 0;
02d1d628 1809
8d14daab
TC
1810 i_img_dim bxmin = seedx;
1811 i_img_dim bxmax = seedx;
1812 i_img_dim bymin = seedy;
1813 i_img_dim bymax = seedy;
02d1d628
AMH
1814
1815 struct llist *st;
1816 struct i_bitmap *btm;
1817
8d14daab
TC
1818 int channels;
1819 i_img_dim xsize,ysize;
3efb0915 1820 i_color cval;
02d1d628 1821
a73aeb5f
AMH
1822 channels = im->channels;
1823 xsize = im->xsize;
1824 ysize = im->ysize;
02d1d628 1825
86d20cb9
AMH
1826 btm = btm_new(xsize, ysize);
1827 st = llist_new(100, sizeof(struct stack_element*));
02d1d628 1828
02d1d628 1829 /* Find the starting span and fill it */
3efb0915
TC
1830 ltx = i_lspan(im, seedx, seedy, seed, cmpfunc);
1831 rtx = i_rspan(im, seedx, seedy, seed, cmpfunc);
aa833c97 1832 for(tx=ltx; tx<=rtx; tx++) SET(tx, seedy);
353eb6e7
TC
1833 bxmin = ltx;
1834 bxmax = rtx;
02d1d628 1835
aa833c97
AMH
1836 ST_PUSH(ltx, rtx, ltx, rtx, seedy+1, 1);
1837 ST_PUSH(ltx, rtx, ltx, rtx, seedy-1, -1);
02d1d628
AMH
1838
1839 while(st->count) {
aa833c97 1840 /* Stack variables */
8d14daab
TC
1841 i_img_dim lx,rx;
1842 i_img_dim dadLx,dadRx;
1843 i_img_dim y;
aa833c97 1844 int direction;
e25e59b1 1845
8d14daab 1846 i_img_dim x;
aa833c97 1847 int wasIn=0;
02d1d628 1848
aa833c97
AMH
1849 ST_POP(); /* sets lx, rx, dadLx, dadRx, y, direction */
1850
1851
1852 if (y<0 || y>ysize-1) continue;
02d1d628
AMH
1853 if (bymin > y) bymin=y; /* in the worst case an extra line */
1854 if (bymax < y) bymax=y;
1855
e25e59b1
AMH
1856
1857 x = lx+1;
3efb0915 1858 if ( lx >= 0 && (wasIn = INSIDE(lx, y, seed)) ) {
aa833c97 1859 SET(lx, y);
02d1d628 1860 lx--;
95b9922f 1861 while(lx >= 0 && INSIDE(lx, y, seed)) {
02d1d628
AMH
1862 SET(lx,y);
1863 lx--;
1864 }
1865 }
1866
86d20cb9 1867 if (bxmin > lx) bxmin = lx;
02d1d628
AMH
1868 while(x <= xsize-1) {
1869 /* printf("x=%d\n",x); */
1870 if (wasIn) {
1871
3efb0915 1872 if (INSIDE(x, y, seed)) {
02d1d628
AMH
1873 /* case 1: was inside, am still inside */
1874 SET(x,y);
1875 } else {
1876 /* case 2: was inside, am no longer inside: just found the
1877 right edge of a span */
aa833c97 1878 ST_STACK(direction, dadLx, dadRx, lx, (x-1), y);
02d1d628 1879
aa833c97 1880 if (bxmax < x) bxmax = x;
02d1d628
AMH
1881 wasIn=0;
1882 }
1883 } else {
aa833c97 1884 if (x > rx) goto EXT;
3efb0915 1885 if (INSIDE(x, y, seed)) {
aa833c97 1886 SET(x, y);
02d1d628 1887 /* case 3: Wasn't inside, am now: just found the start of a new run */
aa833c97
AMH
1888 wasIn = 1;
1889 lx = x;
02d1d628
AMH
1890 } else {
1891 /* case 4: Wasn't inside, still isn't */
1892 }
1893 }
1894 x++;
1895 }
1896 EXT: /* out of loop */
1897 if (wasIn) {
1898 /* hit an edge of the frame buffer while inside a run */
aa833c97
AMH
1899 ST_STACK(direction, dadLx, dadRx, lx, (x-1), y);
1900 if (bxmax < x) bxmax = x;
02d1d628
AMH
1901 }
1902 }
02d1d628 1903
02d1d628 1904 llist_destroy(st);
cc6483e0 1905
aa833c97
AMH
1906 *bxminp = bxmin;
1907 *bxmaxp = bxmax;
1908 *byminp = bymin;
1909 *bymaxp = bymax;
cc6483e0 1910
aa833c97
AMH
1911 return btm;
1912}
cc6483e0 1913
92bda632 1914/*
5715f7c3 1915=item i_flood_fill(C<im>, C<seedx>, C<seedy>, C<color>)
92bda632
TC
1916
1917=category Drawing
1918=synopsis i_flood_fill(im, 50, 50, &color);
cc6483e0 1919
5715f7c3
TC
1920Flood fills the 4-connected region starting from the point (C<seedx>,
1921C<seedy>) with I<color>.
cc6483e0 1922
5715f7c3 1923Returns false if (C<seedx>, C<seedy>) are outside the image.
92bda632
TC
1924
1925=cut
1926*/
cc6483e0 1927
aa833c97 1928undef_int
8d14daab
TC
1929i_flood_fill(i_img *im, i_img_dim seedx, i_img_dim seedy, const i_color *dcol) {
1930 i_img_dim bxmin, bxmax, bymin, bymax;
aa833c97 1931 struct i_bitmap *btm;
8d14daab 1932 i_img_dim x, y;
3efb0915 1933 i_color val;
af22c916 1934 dIMCTXim(im);
dc95a369 1935
857e686a
TC
1936 im_log((aIMCTX, 1, "i_flood_fill(im %p, seed(" i_DFp "), col %p)",
1937 im, i_DFcp(seedx, seedy), dcol));
1938
af22c916 1939 im_clear_error(aIMCTX);
aa833c97
AMH
1940 if (seedx < 0 || seedx >= im->xsize ||
1941 seedy < 0 || seedy >= im->ysize) {
af22c916 1942 im_push_error(aIMCTX, 0, "i_flood_cfill: Seed pixel outside of image");
aa833c97 1943 return 0;
cc6483e0 1944 }
cc6483e0 1945
3efb0915
TC
1946 /* Get the reference color */
1947 i_gpix(im, seedx, seedy, &val);
1948
1949 btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax,
1950 &val, i_ccomp_normal);
cc6483e0 1951
aa833c97
AMH
1952 for(y=bymin;y<=bymax;y++)
1953 for(x=bxmin;x<=bxmax;x++)
1954 if (btm_test(btm,x,y))
1955 i_ppix(im,x,y,dcol);
1956 btm_destroy(btm);
1957 return 1;
cc6483e0
TC
1958}
1959
92bda632 1960/*
5715f7c3 1961=item i_flood_cfill(C<im>, C<seedx>, C<seedy>, C<fill>)
92bda632
TC
1962
1963=category Drawing
1964=synopsis i_flood_cfill(im, 50, 50, fill);
aa833c97 1965
5715f7c3
TC
1966Flood fills the 4-connected region starting from the point (C<seedx>,
1967C<seedy>) with C<fill>.
92bda632 1968
5715f7c3 1969Returns false if (C<seedx>, C<seedy>) are outside the image.
92bda632
TC
1970
1971=cut
1972*/
aa833c97 1973
a321d497 1974undef_int
8d14daab
TC
1975i_flood_cfill(i_img *im, i_img_dim seedx, i_img_dim seedy, i_fill_t *fill) {
1976 i_img_dim bxmin, bxmax, bymin, bymax;
cc6483e0 1977 struct i_bitmap *btm;
3efb0915 1978 i_color val;
af22c916 1979 dIMCTXim(im);
cc6483e0 1980
857e686a
TC
1981 im_log((aIMCTX, 1, "i_flood_cfill(im %p, seed(" i_DFp "), fill %p)",
1982 im, i_DFcp(seedx, seedy), fill));
1983
af22c916 1984 im_clear_error(aIMCTX);
a321d497
AMH
1985
1986 if (seedx < 0 || seedx >= im->xsize ||
1987 seedy < 0 || seedy >= im->ysize) {
af22c916 1988 im_push_error(aIMCTX, 0, "i_flood_cfill: Seed pixel outside of image");
a321d497
AMH
1989 return 0;
1990 }
1991
3efb0915
TC
1992 /* Get the reference color */
1993 i_gpix(im, seedx, seedy, &val);
1994
1995 btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax,
1996 &val, i_ccomp_normal);
1997
1998 cfill_from_btm(im, fill, btm, bxmin, bxmax, bymin, bymax);
1999
2000 btm_destroy(btm);
2001 return 1;
2002}
2003
2004/*
5715f7c3 2005=item i_flood_fill_border(C<im>, C<seedx>, C<seedy>, C<color>, C<border>)
3efb0915
TC
2006
2007=category Drawing
2008=synopsis i_flood_fill_border(im, 50, 50, &color, &border);
2009
5715f7c3
TC
2010Flood fills the 4-connected region starting from the point (C<seedx>,
2011C<seedy>) with C<color>, fill stops when the fill reaches a pixels
2012with color C<border>.
3efb0915 2013
5715f7c3 2014Returns false if (C<seedx>, C<seedy>) are outside the image.
3efb0915
TC
2015
2016=cut
2017*/
2018
2019undef_int
8d14daab 2020i_flood_fill_border(i_img *im, i_img_dim seedx, i_img_dim seedy, const i_color *dcol,
3efb0915 2021 const i_color *border) {
8d14daab 2022 i_img_dim bxmin, bxmax, bymin, bymax;
3efb0915 2023 struct i_bitmap *btm;
8d14daab 2024 i_img_dim x, y;
af22c916 2025 dIMCTXim(im);
dc95a369 2026
857e686a
TC
2027 im_log((aIMCTX, 1, "i_flood_cfill(im %p, seed(" i_DFp "), dcol %p, border %p)",
2028 im, i_DFcp(seedx, seedy), dcol, border));
2029
af22c916 2030 im_clear_error(aIMCTX);
3efb0915
TC
2031 if (seedx < 0 || seedx >= im->xsize ||
2032 seedy < 0 || seedy >= im->ysize) {
af22c916 2033 im_push_error(aIMCTX, 0, "i_flood_cfill: Seed pixel outside of image");
3efb0915
TC
2034 return 0;
2035 }
2036
2037 btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax,
2038 border, i_ccomp_border);
2039
2040 for(y=bymin;y<=bymax;y++)
2041 for(x=bxmin;x<=bxmax;x++)
2042 if (btm_test(btm,x,y))
2043 i_ppix(im,x,y,dcol);
2044 btm_destroy(btm);
2045 return 1;
2046}
2047
2048/*
5715f7c3 2049=item i_flood_cfill_border(C<im>, C<seedx>, C<seedy>, C<fill>, C<border>)
3efb0915
TC
2050
2051=category Drawing
2052=synopsis i_flood_cfill_border(im, 50, 50, fill, border);
2053
5715f7c3
TC
2054Flood fills the 4-connected region starting from the point (C<seedx>,
2055C<seedy>) with C<fill>, the fill stops when it reaches pixels of color
2056C<border>.
3efb0915 2057
5715f7c3 2058Returns false if (C<seedx>, C<seedy>) are outside the image.
3efb0915
TC
2059
2060=cut
2061*/
2062
2063undef_int
8d14daab 2064i_flood_cfill_border(i_img *im, i_img_dim seedx, i_img_dim seedy, i_fill_t *fill,
3efb0915 2065 const i_color *border) {
8d14daab 2066 i_img_dim bxmin, bxmax, bymin, bymax;
3efb0915 2067 struct i_bitmap *btm;
af22c916 2068 dIMCTXim(im);
dc95a369 2069
857e686a
TC
2070 im_log((aIMCTX, 1, "i_flood_cfill_border(im %p, seed(" i_DFp "), fill %p, border %p)",
2071 im, i_DFcp(seedx, seedy), fill, border));
2072
af22c916 2073 im_clear_error(aIMCTX);
3efb0915
TC
2074
2075 if (seedx < 0 || seedx >= im->xsize ||
2076 seedy < 0 || seedy >= im->ysize) {
af22c916 2077 im_push_error(aIMCTX, 0, "i_flood_cfill_border: Seed pixel outside of image");
3efb0915
TC
2078 return 0;
2079 }
2080
2081 btm = i_flood_fill_low(im, seedx, seedy, &bxmin, &bxmax, &bymin, &bymax,
2082 border, i_ccomp_border);
2083
2084 cfill_from_btm(im, fill, btm, bxmin, bxmax, bymin, bymax);
2085
2086 btm_destroy(btm);
2087
2088 return 1;
2089}
2090
2091static void
2092cfill_from_btm(i_img *im, i_fill_t *fill, struct i_bitmap *btm,
8d14daab
TC
2093 i_img_dim bxmin, i_img_dim bxmax, i_img_dim bymin, i_img_dim bymax) {
2094 i_img_dim x, y;
2095 i_img_dim start;
cc6483e0 2096
9b1ec2b8
TC
2097 i_render r;
2098
2099 i_render_init(&r, im, bxmax - bxmin + 1);
2100
2101 for(y=bymin; y<=bymax; y++) {
2102 x = bxmin;
2103 while (x <= bxmax) {
2104 while (x <= bxmax && !btm_test(btm, x, y)) {
2105 ++x;
cc6483e0 2106 }
9b1ec2b8
TC
2107 if (btm_test(btm, x, y)) {
2108 start = x;
2109 while (x <= bxmax && btm_test(btm, x, y)) {
2110 ++x;
2111 }
2112 i_render_fill(&r, start, y, x-start, NULL, fill);
cc6483e0
TC
2113 }
2114 }
cc6483e0 2115 }
9b1ec2b8 2116 i_render_done(&r);
cc6483e0 2117}
12db268a
TC
2118
2119/*
2120=back
2121
2122=cut
2123*/