avoid i_push_errorf() and i_fatal() in a few more places
[imager.git] / hlines.c
CommitLineData
b7c99fa8 1#define IMAGER_NO_CONTEXT
92bda632 2#include "imageri.h"
39f94030
TC
3#include <stdlib.h>
4
5#define OVERLAPPED(start1, end1, start2, end2) \
6 (im_max((start1), (start2)) <= im_min((end1), (end2)))
7
8/*
9=head1 NAME
10
11hlines.c - implements a "class" for managing sets of horizontal line segments
12
13=head1 SYNOPSIS
14
15 i_int_hlines hlines;
16 // just for the specified range of y
17 i_int_init_hlines(&hlines, start_y, count_y, start_x, width_x);
18 // to cover a whole image
19 i_int_init_hlines_img(&hlines, img);
20 // add a hline segment, merging into existing
21 i_int_hlines_add(&hlines, y, x, width);
22
23 // work over the lines
24 for (y = hlines.start; y < hlines.limit; ++y) {
25 i_int_hline_entry *entry = hlines.entries[i];
26 if (entry) {
27 for (i = 0; i < entry->count; ++i) {
28 i_int_hline_seg *seg = entry->segs+i;
29 // do something on line y for seg->minx to x_limit
30 }
31 }
32 }
33
34 // free it all up
35 i_int_hlines_destroy(&hlines);
36
37=head1 DESCRIPTION
38
39Provides a class to manage sets of horizontal line segments. The
40intent is that when drawing shapes where the algorithm used might
41cause overlaps we can use this class to resolve the overlaps.
42
43Note that segment lists are intended to remain small, if we end up
44with a need for longer lists we should use different structure for the
45segment lists.
46
47=over
48
49=item i_int_init_hlines
50
51i_int_init_hlines(&hlines, start_y, count_y, start_x, width_x)
52
53Initializes the structure based on drawing an object within the given
54range. Any x or y values outside the given ranges will be ignored.
55
56=cut
57
58*/
59
60void
61i_int_init_hlines(
62 i_int_hlines *hlines,
8d14daab
TC
63 i_img_dim start_y,
64 i_img_dim count_y,
65 i_img_dim start_x,
66 i_img_dim width_x
39f94030
TC
67 )
68{
8d14daab 69 size_t bytes = count_y * sizeof(i_int_hline_entry *);
39f94030
TC
70
71 if (bytes / count_y != sizeof(i_int_hline_entry *)) {
d8ea80b1
TC
72 dIMCTX;
73 im_fatal(aIMCTX, 3, "integer overflow calculating memory allocation\n");
39f94030
TC
74 }
75
76 hlines->start_y = start_y;
77 hlines->limit_y = start_y + count_y;
78 hlines->start_x = start_x;
79 hlines->limit_x = start_x + width_x;
80 hlines->entries = mymalloc(bytes);
81 memset(hlines->entries, 0, bytes);
82}
83
84/*
85=item i_int_init_hlines_img
86
87i_int_init_hlines_img(img);
88
89Initialize a hlines object as if we could potentially draw anywhere on
90the image.
91
92=cut
93*/
94
95void
96i_int_init_hlines_img(i_int_hlines *hlines, i_img *img)
97{
98 i_int_init_hlines(hlines, 0, img->ysize, 0, img->xsize);
99}
100
101/*
102=item i_int_hlines_add
103
104i_int_hlines_add(hlines, y, x, width)
105
106Add to the list, merging with existing entries.
107
108=cut
109*/
110
111void
8d14daab
TC
112i_int_hlines_add(i_int_hlines *hlines, i_img_dim y, i_img_dim x, i_img_dim width) {
113 i_img_dim x_limit = x + width;
39f94030
TC
114
115 if (width < 0) {
d8ea80b1
TC
116 dIMCTX;
117 im_fatal(aIMCTX, 3, "negative width %d passed to i_int_hlines_add\n", width);
39f94030
TC
118 }
119
120 /* just return if out of range */
121 if (y < hlines->start_y || y >= hlines->limit_y)
122 return;
123
124 if (x >= hlines->limit_x || x_limit < hlines->start_x)
125 return;
126
127 /* adjust x to our range */
128 if (x < hlines->start_x)
129 x = hlines->start_x;
130 if (x_limit > hlines->limit_x)
131 x_limit = hlines->limit_x;
132
133 if (x == x_limit)
134 return;
135
136 if (hlines->entries[y - hlines->start_y]) {
137 i_int_hline_entry *entry = hlines->entries[y - hlines->start_y];
8d14daab 138 i_img_dim i, found = -1;
39f94030
TC
139
140 for (i = 0; i < entry->count; ++i) {
141 i_int_hline_seg *seg = entry->segs + i;
142 if (OVERLAPPED(x, x_limit, seg->minx, seg->x_limit)) {
143 found = i;
144 break;
145 }
146 }
147 if (found >= 0) {
148 /* ok, we found an overlapping segment, any other overlapping
149 segments need to be merged into the one we found */
150 i_int_hline_seg *merge_seg = entry->segs + found;
151
152 /* merge in the segment we found */
153 x = im_min(x, merge_seg->minx);
154 x_limit = im_max(x_limit, merge_seg->x_limit);
155
156 /* look for other overlapping segments */
157 /* this could be a for(), but I'm using continue */
158 i = found + 1;
159 while (i < entry->count) {
160 i_int_hline_seg *seg = entry->segs + i;
161 if (OVERLAPPED(x, x_limit, seg->minx, seg->x_limit)) {
162 /* merge this into the current working segment, then
163 delete it by moving the last segment (if this isn't it)
164 into it's place */
165 x = im_min(x, seg->minx);
166 x_limit = im_max(x_limit, seg->x_limit);
167 if (i < entry->count-1) {
168 *seg = entry->segs[entry->count-1];
169 --entry->count;
170 continue;
171 }
172 else {
173 --entry->count;
174 break;
175 }
176 }
177 ++i;
178 }
179
180 /* store it back */
181 merge_seg->minx = x;
182 merge_seg->x_limit = x_limit;
183 }
184 else {
185 i_int_hline_seg *seg;
186 /* add a new segment */
187 if (entry->count == entry->alloc) {
188 /* expand it */
8d14daab 189 size_t alloc = entry->alloc * 3 / 2;
39f94030
TC
190 entry = myrealloc(entry, sizeof(i_int_hline_entry) +
191 sizeof(i_int_hline_seg) * (alloc - 1));
192 entry->alloc = alloc;
193 hlines->entries[y - hlines->start_y] = entry;
194 }
195 seg = entry->segs + entry->count++;
196 seg->minx = x;
197 seg->x_limit = x_limit;
198 }
199 }
200 else {
201 /* make a new one - start with space for 10 */
202 i_int_hline_entry *entry = mymalloc(sizeof(i_int_hline_entry) +
203 sizeof(i_int_hline_seg) * 9);
204 entry->alloc = 10;
205 entry->count = 1;
206 entry->segs[0].minx = x;
207 entry->segs[0].x_limit = x_limit;
208 hlines->entries[y - hlines->start_y] = entry;
209 }
210}
211
212/*
213=item i_int_hlines_destroy
214
215i_int_hlines_destroy(&hlines)
216
217Releases all memory associated with the structure.
218
219=cut
220*/
221
222void
223i_int_hlines_destroy(i_int_hlines *hlines) {
8d14daab
TC
224 size_t entry_count = hlines->limit_y - hlines->start_y;
225 size_t i;
39f94030
TC
226
227 for (i = 0; i < entry_count; ++i) {
228 if (hlines->entries[i])
229 myfree(hlines->entries[i]);
230 }
231 myfree(hlines->entries);
232}
233
234/*
235=item i_int_hlines_fill_color
236
237i_int_hlines_fill(im, hlines, color)
238
239Fill the areas given by hlines with color.
240
241=cut
242*/
243
244void
97ac0a96 245i_int_hlines_fill_color(i_img *im, i_int_hlines *hlines, const i_color *col) {
8d14daab 246 i_img_dim y, i, x;
39f94030
TC
247
248 for (y = hlines->start_y; y < hlines->limit_y; ++y) {
249 i_int_hline_entry *entry = hlines->entries[y - hlines->start_y];
250 if (entry) {
251 for (i = 0; i < entry->count; ++i) {
252 i_int_hline_seg *seg = entry->segs + i;
253 for (x = seg->minx; x < seg->x_limit; ++x) {
254 i_ppix(im, x, y, col);
255 }
256 }
257 }
258 }
259}
260
261/*
262=item i_int_hlines_fill_fill
263
264i_int_hlines_fill_fill(im, hlines, fill)
265
266*/
267void
268i_int_hlines_fill_fill(i_img *im, i_int_hlines *hlines, i_fill_t *fill) {
9b1ec2b8 269 i_render r;
8d14daab 270 i_img_dim y, i;
39f94030 271
9b1ec2b8
TC
272 i_render_init(&r, im, im->xsize);
273
274 for (y = hlines->start_y; y < hlines->limit_y; ++y) {
275 i_int_hline_entry *entry = hlines->entries[y - hlines->start_y];
276 if (entry) {
277 for (i = 0; i < entry->count; ++i) {
278 i_int_hline_seg *seg = entry->segs + i;
8d14daab 279 i_img_dim width = seg->x_limit-seg->minx;
9b1ec2b8
TC
280
281 i_render_fill(&r, seg->minx, y, width, NULL, fill);
282 }
283 }
284 }
285 i_render_done(&r);
286
287#if 1
288#else
39f94030
TC
289 if (im->bits == i_8_bits && fill->fill_with_color) {
290 i_color *line = mymalloc(sizeof(i_color) * im->xsize);
291 i_color *work = NULL;
292 if (fill->combine)
293 work = mymalloc(sizeof(i_color) * im->xsize);
294 for (y = hlines->start_y; y < hlines->limit_y; ++y) {
295 i_int_hline_entry *entry = hlines->entries[y - hlines->start_y];
296 if (entry) {
297 for (i = 0; i < entry->count; ++i) {
298 i_int_hline_seg *seg = entry->segs + i;
8d14daab 299 i_img_dim width = seg->x_limit-seg->minx;
39f94030
TC
300
301 if (fill->combine) {
302 i_glin(im, seg->minx, seg->x_limit, y, line);
303 (fill->fill_with_color)(fill, seg->minx, y, width,
304 im->channels, work);
305 (fill->combine)(line, work, im->channels, width);
306 }
307 else {
308 (fill->fill_with_color)(fill, seg->minx, y, width,
309 im->channels, line);
310 }
311 i_plin(im, seg->minx, seg->x_limit, y, line);
312 }
313 }
314 }
315
316 myfree(line);
317 if (work)
318 myfree(work);
319 }
320 else {
321 i_fcolor *line = mymalloc(sizeof(i_fcolor) * im->xsize);
322 i_fcolor *work = NULL;
323 if (fill->combinef)
324 work = mymalloc(sizeof(i_fcolor) * im->xsize);
325 for (y = hlines->start_y; y < hlines->limit_y; ++y) {
326 i_int_hline_entry *entry = hlines->entries[y - hlines->start_y];
327 if (entry) {
328 for (i = 0; i < entry->count; ++i) {
329 i_int_hline_seg *seg = entry->segs + i;
8d14daab 330 i_img_dim width = seg->x_limit-seg->minx;
39f94030
TC
331
332 if (fill->combinef) {
333 i_glinf(im, seg->minx, seg->x_limit, y, line);
334 (fill->fill_with_fcolor)(fill, seg->minx, y, width,
335 im->channels, work);
336 (fill->combinef)(line, work, im->channels, width);
337 }
338 else {
339 (fill->fill_with_fcolor)(fill, seg->minx, y, width,
340 im->channels, line);
341 }
342 i_plinf(im, seg->minx, seg->x_limit, y, line);
343 }
344 }
345 }
346
347 myfree(line);
348 if (work)
349 myfree(work);
350 }
9b1ec2b8 351#endif
39f94030
TC
352}
353
354/*
355=back
356
357=head1 AUTHOR
358
5b480b14 359Tony Cook <tonyc@cpan.org>
39f94030
TC
360
361=head1 REVISION
362
363$Revision$
364
365=cut
366*/