support for generic fills for box and arc, with solid, hatched
[imager.git] / fills.c
CommitLineData
f1ac5027
TC
1#include "image.h"
2#include "imagei.h"
3
4/*
5
6Possible fill types:
7 - solid colour
8 - hatched (pattern, fg, bg)
9 - tiled image
10 - regmach
11 - tiling?
12 - generic?
13
14*/
15
16static i_color fcolor_to_color(i_fcolor *c) {
17 int ch;
18 i_color out;
19
20 for (ch = 0; ch < MAXCHANNELS; ++ch)
21 out.channel[ch] = SampleFTo8(c->channel[ch]);
22}
23
24static i_fcolor color_to_fcolor(i_color *c) {
25 int ch;
26 i_color out;
27
28 for (ch = 0; ch < MAXCHANNELS; ++ch)
29 out.channel[ch] = Sample8ToF(c->channel[ch]);
30}
31
32typedef struct
33{
34 i_fill_t base;
35 i_color c;
36 i_fcolor fc;
37} i_fill_solid_t;
38
39#define COMBINE(out, in, channels) \
40 { \
41 int ch; \
42 for (ch = 0; ch < (channels); ++ch) { \
43 (out).channel[ch] = ((out).channel[ch] * (255 - (in).channel[3]) \
44 + (in).channel[ch] * (in).channel[3]) / 255; \
45 } \
46 }
47
48#define COMBINEF(out, in, channels) \
49 { \
50 int ch; \
51 for (ch = 0; ch < (channels); ++ch) { \
52 (out).channel[ch] = (out).channel[ch] * (1.0 - (in).channel[3]) \
53 + (in).channel[ch] * (in).channel[3]; \
54 } \
55 }
56
57static void fill_solid(i_fill_t *, int x, int y, int width, int channels,
58 i_color *);
59static void fill_solidf(i_fill_t *, int x, int y, int width, int channels,
60 i_fcolor *);
61static void fill_solid_comb(i_fill_t *, int x, int y, int width, int channels,
62 i_color *);
63static void fill_solidf_comb(i_fill_t *, int x, int y, int width,
64 int channels, i_fcolor *);
65
66static i_fill_solid_t base_solid_fill =
67{
68 {
69 fill_solid,
70 fill_solidf,
71 NULL,
72 0
73 },
74};
75static i_fill_solid_t base_solid_fill_comb =
76{
77 {
78 fill_solid_comb,
79 fill_solidf_comb,
80 NULL,
81 1
82 },
83};
84
85void
86i_fill_destroy(i_fill_t *fill) {
87 if (fill->destroy)
88 (fill->destroy)(fill);
89 myfree(fill);
90}
91
92i_fill_t *
93i_new_fill_solidf(i_fcolor *c, int combine) {
94 int ch;
95 i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t));
96
97 if (combine && c->channel[3] < 1.0)
98 *fill = base_solid_fill_comb;
99 else
100 *fill = base_solid_fill;
101 fill->fc = *c;
102 for (ch = 0; ch < MAXCHANNELS; ++ch) {
103 fill->c.channel[ch] = SampleFTo8(c->channel[ch]);
104 }
105
106 return &fill->base;
107}
108
109i_fill_t *
110i_new_fill_solid(i_color *c, int combine) {
111 int ch;
112 i_fill_solid_t *fill = mymalloc(sizeof(i_fill_solid_t));
113
114 if (combine && c->channel[3] < 255)
115 *fill = base_solid_fill_comb;
116 else
117 *fill = base_solid_fill;
118 fill->c = *c;
119 for (ch = 0; ch < MAXCHANNELS; ++ch) {
120 fill->fc.channel[ch] = Sample8ToF(c->channel[ch]);
121 }
122
123 return &fill->base;
124}
125
126#define T_SOLID_FILL(fill) ((i_fill_solid_t *)(fill))
127
128static void
129fill_solid(i_fill_t *fill, int x, int y, int width, int channels,
130 i_color *data) {
131 while (width-- > 0) {
132 *data++ = T_SOLID_FILL(fill)->c;
133 }
134}
135
136static void
137fill_solidf(i_fill_t *fill, int x, int y, int width, int channels,
138 i_fcolor *data) {
139 while (width-- > 0) {
140 *data++ = T_SOLID_FILL(fill)->fc;
141 }
142}
143
144static void
145fill_solid_comb(i_fill_t *fill, int x, int y, int width, int channels,
146 i_color *data) {
147 i_color c = T_SOLID_FILL(fill)->c;
148
149 while (width-- > 0) {
150 COMBINE(*data, c, channels);
151 ++data;
152 }
153}
154
155static void
156fill_solidf_comb(i_fill_t *fill, int x, int y, int width, int channels,
157 i_fcolor *data) {
158 i_fcolor c = T_SOLID_FILL(fill)->fc;
159
160 while (width-- > 0) {
161 COMBINEF(*data, c, channels);
162 ++data;
163 }
164}
165
166static unsigned char
167builtin_hatches[][8] =
168{
169 {
170 /* 1x1 checkerboard */
171 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55,
172 },
173 {
174 /* 2x2 checkerboard */
175 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33,
176 },
177 {
178 /* 4 x 4 checkerboard */
179 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F,
180 },
181 {
182 /* single vertical lines */
183 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
184 },
185 {
186 /* double vertical lines */
187 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
188 },
189 {
190 /* quad vertical lines */
191 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
192 },
193 {
194 /* single hlines */
195 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
196 },
197 {
198 /* double hlines */
199 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
200 },
201 {
202 /* quad hlines */
203 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00,
204 },
205 {
206 /* single / */
207 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
208 },
209 {
210 /* single \ */
211 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01,
212 },
213 {
214 /* double / */
215 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88,
216 },
217 {
218 /* double \ */
219 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11,
220 },
221 {
222 /* single grid */
223 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
224 },
225 {
226 /* double grid */
227 0xFF, 0x88, 0x88, 0x88, 0xFF, 0x88, 0x88, 0x88,
228 },
229 {
230 /* quad grid */
231 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA,
232 },
233 {
234 /* single dots */
235 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
236 },
237 {
238 /* 4 dots */
239 0x88, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
240 },
241 {
242 /* 16 dots */
243 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00,
244 },
245 {
246 /* simple stipple */
247 0x48, 0x84, 0x00, 0x00, 0x84, 0x48, 0x00, 0x00,
248 },
249 {
250 /* weave */
251 0x55, 0xFD, 0x05, 0xFD, 0x55, 0xDF, 0x50, 0xDF,
252 },
253 {
254 /* single cross hatch */
255 0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82, 0x01,
256 },
257 {
258 /* double cross hatch */
259 0xAA, 0x44, 0xAA, 0x11, 0xAA, 0x44, 0xAA, 0x11,
260 },
261 {
262 /* vertical lozenge */
263 0x11, 0x11, 0x11, 0xAA, 0x44, 0x44, 0x44, 0xAA,
264 },
265 {
266 /* horizontal lozenge */
267 0x88, 0x70, 0x88, 0x07, 0x88, 0x70, 0x88, 0x07,
268 },
269 {
270 /* scales overlapping downwards */
271 0x77, 0x22, 0x22, 0x22, 0xDD, 0x88, 0x88, 0x88,
272 },
273 {
274 /* scales overlapping upwards */
275 0x22, 0x22, 0x22, 0x77, 0x88, 0x88, 0x88, 0xDD,
276 },
277 {
278 /* scales overlapping leftwards */
279 0xF0, 0x11, 0x0F, 0x11, 0xF0, 0x11, 0x0F, 0x11,
280 },
281 {
282 /* scales overlapping rightwards */
283 0x88, 0xF0, 0x88, 0x0F, 0x88, 0xF0, 0x88, 0x0F,
284 },
285 {
286 /* denser stipple */
287 0x44, 0x88, 0x22, 0x11, 0x44, 0x88, 0x22, 0x11,
288 },
289 {
290 /* L-shaped tiles */
291 0xFF, 0x84, 0x84, 0x9C, 0x94, 0x9C, 0x90, 0x90,
292 },
293};
294
295typedef struct
296{
297 i_fill_t base;
298 i_color fg, bg;
299 i_fcolor ffg, fbg;
300 unsigned char hatch[8];
301 int dx, dy;
302} i_fill_hatch_t;
303
304static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels,
305 i_color *data);
306static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels,
307 i_fcolor *data);
308
309static
310i_fill_t *
311i_new_hatch_low(i_color *fg, i_color *bg, i_fcolor *ffg, i_fcolor *fbg,
312 int combine, int hatch, unsigned char *cust_hatch,
313 int dx, int dy) {
314 i_fill_hatch_t *fill = mymalloc(sizeof(i_fill_hatch_t));
315
316 fill->base.fill_with_color = fill_hatch;
317 fill->base.fill_with_fcolor = fill_hatchf;
318 fill->base.destroy = NULL;
319 fill->fg = fg ? *fg : fcolor_to_color(ffg);
320 fill->bg = bg ? *bg : fcolor_to_color(fbg);
321 fill->ffg = ffg ? *ffg : color_to_fcolor(fg);
322 fill->fbg = fbg ? *fbg : color_to_fcolor(bg);
323 fill->base.combines =
324 combine && (fill->ffg.channel[0] < 1 || fill->fbg.channel[0] < 1);
325 if (cust_hatch) {
326 memcpy(fill->hatch, cust_hatch, 8);
327 }
328 else {
329 if (hatch > sizeof(builtin_hatches)/sizeof(*builtin_hatches))
330 hatch = 0;
331 memcpy(fill->hatch, builtin_hatches[hatch], 8);
332 }
333 fill->dx = dx & 7;
334 fill->dy = dy & 7;
335
336 return &fill->base;
337}
338
339i_fill_t *
340i_new_fill_hatch(i_color *fg, i_color *bg, int combine, int hatch,
341 unsigned char *cust_hatch, int dx, int dy) {
342 return i_new_hatch_low(fg, bg, NULL, NULL, combine, hatch, cust_hatch,
343 dx, dy);
344}
345
346i_fill_t *
347i_new_fill_hatchf(i_fcolor *fg, i_fcolor *bg, int combine, int hatch,
348 unsigned char *cust_hatch, int dx, int dy) {
349 return i_new_hatch_low(NULL, NULL, fg, bg, combine, hatch, cust_hatch,
350 dx, dy);
351}
352
353static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels,
354 i_color *data) {
355 i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
356 int byte = f->hatch[(y + f->dy) & 7];
357 int xpos = (x + f->dx) & 7;
358 int mask = 128 >> xpos;
359
360 while (width-- > 0) {
361 i_color c = (byte & mask) ? f->fg : f->bg;
362
363 if (f->base.combines) {
364 COMBINE(*data, c, channels);
365 }
366 else {
367 *data = c;
368 }
369 ++data;
370 if ((mask >>= 1) == 0)
371 mask = 128;
372 }
373}
374
375static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels,
376 i_fcolor *data) {
377 i_fill_hatch_t *f = (i_fill_hatch_t *)fill;
378 int byte = f->hatch[(y + f->dy) & 7];
379 int xpos = (x + f->dx) & 7;
380 int mask = 128 >> xpos;
381
382 while (width-- > 0) {
383 i_fcolor c = (byte & mask) ? f->ffg : f->fbg;
384
385 if (f->base.combines) {
386 COMBINE(*data, c, channels);
387 }
388 else {
389 *data = c;
390 }
391 ++data;
392 if ((mask >>= 1) == 0)
393 mask = 128;
394 }
395}