]> git.imager.perl.org - imager.git/blob - fills.c
bb9700c68b880d48106e15f172133c7fccd95f65
[imager.git] / fills.c
1 #include "image.h"
2 #include "imagei.h"
3
4 /*
5
6 Possible fill types:
7  - solid colour
8  - hatched (pattern, fg, bg)
9  - tiled image
10  - regmach
11  - tiling?
12  - generic?
13
14 */
15
16 static 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
24 static 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
32 typedef 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
57 static void fill_solid(i_fill_t *, int x, int y, int width, int channels, 
58                        i_color *);
59 static void fill_solidf(i_fill_t *, int x, int y, int width, int channels, 
60                         i_fcolor *);
61 static void fill_solid_comb(i_fill_t *, int x, int y, int width, int channels, 
62                             i_color *);
63 static void fill_solidf_comb(i_fill_t *, int x, int y, int width, 
64                              int channels, i_fcolor *);
65
66 static i_fill_solid_t base_solid_fill =
67 {
68   {
69     fill_solid,
70     fill_solidf,
71     NULL,
72     0
73   },
74 };
75 static 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
85 void
86 i_fill_destroy(i_fill_t *fill) {
87   if (fill->destroy)
88     (fill->destroy)(fill);
89   myfree(fill);
90 }
91
92 i_fill_t *
93 i_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
109 i_fill_t *
110 i_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
128 static void
129 fill_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
136 static void
137 fill_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
144 static void
145 fill_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
155 static void
156 fill_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
166 static unsigned char
167 builtin_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     0x80, 0x80, 0x41, 0x3E, 0x08, 0x08, 0x14, 0xE3,
272   },
273   {
274     /* scales overlapping upwards */
275     0xC7, 0x28, 0x10, 0x10, 0x7C, 0x82, 0x01, 0x01,
276   },
277   {
278     /* scales overlapping leftwards */
279     0x83, 0x84, 0x88, 0x48, 0x38, 0x48, 0x88, 0x84,
280   },
281   {
282     /* scales overlapping rightwards */
283     0x21, 0x11, 0x12, 0x1C, 0x12, 0x11, 0x21, 0xC1,
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
295 typedef 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
304 static void fill_hatch(i_fill_t *fill, int x, int y, int width, int channels, 
305                        i_color *data);
306 static void fill_hatchf(i_fill_t *fill, int x, int y, int width, int channels, 
307                         i_fcolor *data);
308
309 static
310 i_fill_t *
311 i_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
339 i_fill_t *
340 i_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
346 i_fill_t *
347 i_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
353 static 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
375 static 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 }