add new comparison method rgb_difference that resembles arithmetical difference per...
[imager.git] / context.c
CommitLineData
74315ca9 1#include "imageri.h"
31a13473 2#include <stdio.h>
74315ca9 3
d7506bda
TC
4static volatile im_slot_t slot_count = 1;
5static im_slot_destroy_t *volatile slot_destructors;
6static volatile i_mutex_t slot_mutex;
fc02e376 7
74315ca9
TC
8/*
9=item im_context_new()
10
11Create a new Imager context object.
12
13=cut
14*/
15
16im_context_t
17im_context_new(void) {
cf8c77ae 18 im_context_t ctx = malloc(sizeof(im_context_struct));
74315ca9
TC
19 int i;
20
fc02e376
TC
21 if (!slot_mutex)
22 slot_mutex = i_mutex_new();
23
cf8c77ae
TC
24 if (!ctx)
25 return NULL;
26
74315ca9
TC
27 ctx->error_sp = IM_ERROR_COUNT-1;
28 for (i = 0; i < IM_ERROR_COUNT; ++i) {
29 ctx->error_alloc[i] = 0;
30 ctx->error_stack[i].msg = NULL;
31 ctx->error_stack[i].code = 0;
32 }
33#ifdef IMAGER_LOG
34 ctx->log_level = 0;
35 ctx->lg_file = NULL;
36#endif
44d86483
TC
37 ctx->max_width = 0;
38 ctx->max_height = 0;
cf8c77ae 39 ctx->max_bytes = DEF_BYTES_LIMIT;
74315ca9 40
f4ced639
TC
41 ctx->slot_alloc = slot_count;
42 ctx->slots = calloc(sizeof(void *), ctx->slot_alloc);
fc02e376
TC
43 if (!ctx->slots) {
44 free(ctx);
45 return NULL;
46 }
47
b7028a2e
TC
48 ctx->file_magic = NULL;
49
31a13473
TC
50 ctx->refcount = 1;
51
52#ifdef IMAGER_TRACE_CONTEXT
53 fprintf(stderr, "im_context: created %p\n", ctx);
54#endif
55
fc02e376 56
74315ca9
TC
57 return ctx;
58}
59
60/*
31a13473 61=item im_context_refinc(ctx, where)
abffffed
TC
62X<im_context_refinc API>
63=section Context objects
64=synopsis im_context_refinc(aIMCTX, "a description");
31a13473
TC
65
66Add a new reference to the context.
67
68=cut
69*/
70
71void
72im_context_refinc(im_context_t ctx, const char *where) {
73 ++ctx->refcount;
74
75#ifdef IMAGER_TRACE_CONTEXT
76 fprintf(stderr, "im_context:%s: refinc %p (count now %lu)\n", where,
77 ctx, (unsigned long)ctx->refcount);
78#endif
79}
80
81/*
abffffed
TC
82=item im_context_refdec(ctx, where)
83X<im_context_refdec API>
84=section Context objects
85=synopsis im_context_refdec(aIMCTX, "a description");
74315ca9 86
abffffed
TC
87Remove a reference to the context, releasing it if all references have
88been removed.
74315ca9
TC
89
90=cut
91*/
92
93void
31a13473 94im_context_refdec(im_context_t ctx, const char *where) {
74315ca9 95 int i;
fc02e376 96 im_slot_t slot;
74315ca9 97
31a13473
TC
98 im_assert(ctx->refcount > 0);
99
100 --ctx->refcount;
101
102#ifdef IMAGER_TRACE_CONTEXT
103 fprintf(stderr, "im_context:%s: delete %p (count now %lu)\n", where,
104 ctx, (unsigned long)ctx->refcount);
105#endif
106
107 if (ctx->refcount != 0)
108 return;
109
d7506bda
TC
110 /* lock here to avoid slot_destructors from being moved under us */
111 i_mutex_lock(slot_mutex);
fc02e376
TC
112 for (slot = 0; slot < ctx->slot_alloc; ++slot) {
113 if (ctx->slots[slot] && slot_destructors[slot])
114 slot_destructors[slot](ctx->slots[slot]);
115 }
d7506bda 116 i_mutex_unlock(slot_mutex);
fc02e376
TC
117
118 free(ctx->slots);
119
74315ca9
TC
120 for (i = 0; i < IM_ERROR_COUNT; ++i) {
121 if (ctx->error_stack[i].msg)
122 myfree(ctx->error_stack[i].msg);
123 }
b7028a2e
TC
124
125 {
126 im_file_magic *p = ctx->file_magic;
127 while (p != NULL) {
128 im_file_magic *n = p->next;
129 free(p->m.name);
130 free(p->m.magic);
131 free(p->m.mask);
132 free(p);
133 p = n;
134 }
135 }
74315ca9 136#ifdef IMAGER_LOG
6ee0c41f 137 if (ctx->lg_file && ctx->own_log)
74315ca9
TC
138 fclose(ctx->lg_file);
139#endif
cf8c77ae
TC
140
141 free(ctx);
74315ca9
TC
142}
143
144/*
145=item im_context_clone(ctx)
146
147Clone an Imager context object, returning the result.
148
a6c8996f
TC
149The error stack is not copied from the original context.
150
74315ca9
TC
151=cut
152*/
153
154im_context_t
31a13473 155im_context_clone(im_context_t ctx, const char *where) {
cf8c77ae 156 im_context_t nctx = malloc(sizeof(im_context_struct));
74315ca9
TC
157 int i;
158
cf8c77ae
TC
159 if (!nctx)
160 return NULL;
161
d7506bda
TC
162 nctx->slot_alloc = slot_count;
163 nctx->slots = calloc(sizeof(void *), nctx->slot_alloc);
fc02e376
TC
164 if (!nctx->slots) {
165 free(nctx);
166 return NULL;
167 }
fc02e376 168
a6c8996f 169 nctx->error_sp = IM_ERROR_COUNT-1;
74315ca9 170 for (i = 0; i < IM_ERROR_COUNT; ++i) {
a6c8996f
TC
171 nctx->error_alloc[i] = 0;
172 nctx->error_stack[i].msg = NULL;
74315ca9
TC
173 }
174#ifdef IMAGER_LOG
175 nctx->log_level = ctx->log_level;
176 if (ctx->lg_file) {
6ee0c41f
TC
177 if (ctx->own_log) {
178 int newfd = dup(fileno(ctx->lg_file));
9ca939d0
TC
179 if (newfd >= 0) {
180 nctx->own_log = 1;
181 nctx->lg_file = fdopen(newfd, "w");
182 if (nctx->lg_file)
183 setvbuf(nctx->lg_file, NULL, _IONBF, BUFSIZ);
184 }
185 else {
186#ifdef IMAGER_TRACE_CONTEXT
187 perror("im_context:failed to clone log");
188#endif
189 free(nctx->slots);
190 free(nctx);
191 return NULL;
192 }
6ee0c41f
TC
193 }
194 else {
195 /* stderr */
196 nctx->lg_file = ctx->lg_file;
197 nctx->own_log = 0;
198 }
74315ca9
TC
199 }
200 else {
44d86483 201 nctx->lg_file = NULL;
74315ca9
TC
202 }
203#endif
44d86483
TC
204 nctx->max_width = ctx->max_width;
205 nctx->max_height = ctx->max_height;
206 nctx->max_bytes = ctx->max_bytes;
fc02e376 207
31a13473 208 nctx->refcount = 1;
74315ca9 209
b7028a2e
TC
210 {
211 im_file_magic *inp = ctx->file_magic;
212 im_file_magic **outpp = &nctx->file_magic;
213 *outpp = NULL;
214 while (inp) {
215 im_file_magic *m = malloc(sizeof(im_file_magic));
216 if (!m) {
217 /* should free any parts of the list already allocated */
218 im_context_refdec(nctx, "failed cloning");
219 return NULL;
220 }
221 m->next = NULL;
222 m->m.name = strdup(inp->m.name);
223 m->m.magic_size = inp->m.magic_size;
224 m->m.magic = malloc(inp->m.magic_size);
225 m->m.mask = malloc(inp->m.magic_size);
226 if (m->m.name == NULL || m->m.magic == NULL || m->m.mask == NULL) {
227 free(m->m.name);
228 free(m->m.magic);
229 free(m->m.mask);
230 free(m);
231 im_context_refdec(nctx, "failed cloning");
232 return NULL;
233 }
234 memcpy(m->m.magic, inp->m.magic, m->m.magic_size);
235 memcpy(m->m.mask, inp->m.mask, m->m.magic_size);
236 *outpp = m;
237 outpp = &m->next;
238 inp = inp->next;
239 }
240 }
241
31a13473
TC
242#ifdef IMAGER_TRACE_CONTEXT
243 fprintf(stderr, "im_context:%s: cloned %p to %p\n", where, ctx, nctx);
244#endif
245
246 return nctx;
74315ca9 247}
fc02e376
TC
248
249/*
250=item im_context_slot_new(destructor)
251
252Allocate a new context-local-storage slot.
253
c135e76b
TC
254C<desctructor> will be called when the context is destroyed if the
255corresponding slot is non-NULL.
256
fc02e376
TC
257=cut
258*/
259
260im_slot_t
261im_context_slot_new(im_slot_destroy_t destructor) {
262 im_slot_t new_slot;
263 im_slot_destroy_t *new_destructors;
264 if (!slot_mutex)
265 slot_mutex = i_mutex_new();
266
267 i_mutex_lock(slot_mutex);
268
269 new_slot = slot_count++;
270 new_destructors = realloc(slot_destructors, sizeof(void *) * slot_count);
271 if (!new_destructors)
272 i_fatal(1, "Cannot allocate memory for slot destructors");
273 slot_destructors = new_destructors;
274
275 slot_destructors[new_slot] = destructor;
276
277 i_mutex_unlock(slot_mutex);
278
279 return new_slot;
280}
281
282/*
283=item im_context_slot_set(slot, value)
284
285Set the value of a slot.
286
287Returns true on success.
288
289Aborts if the slot supplied is invalid.
290
291If reallocation of slot storage fails, returns false.
292
293=cut
294*/
295
296int
297im_context_slot_set(im_context_t ctx, im_slot_t slot, void *value) {
298 if (slot < 0 || slot >= slot_count) {
299 fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n",
300 (int)slot, (int)slot_count-1);
301 abort();
302 }
303
304 if (slot >= ctx->slot_alloc) {
305 ssize_t i;
306 size_t new_alloc = slot_count;
307 void **new_slots = realloc(ctx->slots, sizeof(void *) * new_alloc);
308
309 if (!new_slots)
310 return 0;
311
312 for (i = ctx->slot_alloc; i < new_alloc; ++i)
313 new_slots[i] = NULL;
314
315 ctx->slots = new_slots;
316 ctx->slot_alloc = new_alloc;
317 }
318
319 ctx->slots[slot] = value;
320
321 return 1;
322}
323
324/*
325=item im_context_slot_get(ctx, slot)
326
327Retrieve the value previously stored in the given slot of the context
328object.
329
330=cut
331*/
332
333void *
334im_context_slot_get(im_context_t ctx, im_slot_t slot) {
335 if (slot < 0 || slot >= slot_count) {
336 fprintf(stderr, "Invalid slot %d (valid 0 - %d)\n",
337 (int)slot, (int)slot_count-1);
338 abort();
339 }
340
341 if (slot >= ctx->slot_alloc)
342 return NULL;
343
344 return ctx->slots[slot];
345}
b7028a2e
TC
346
347/*
348=item im_add_file_magic(ctx, name, bits, mask, length)
349
350Add file type magic to the given context.
351
352=cut
353*/
354
355int
356im_add_file_magic(im_context_t ctx, const char *name,
357 const unsigned char *bits, const unsigned char *mask,
358 size_t length) {
359 im_file_magic *m = malloc(sizeof(im_file_magic));
360
361 if (m == NULL)
362 return 0;
363
364 if (length > 512)
365 length = 512;
366
367 m->m.name = strdup(name);
368 m->m.magic = malloc(length);
369 m->m.mask = malloc(length);
370 m->m.magic_size = length;
371
372 if (name == NULL || bits == NULL || mask == NULL) {
373 free(m->m.name);
374 free(m->m.magic);
375 free(m->m.mask);
376 free(m);
377 return 0;
378 }
379 memcpy(m->m.magic, bits, length);
380 memcpy(m->m.mask, mask, length);
381 m->next = ctx->file_magic;
382 ctx->file_magic = m;
383
384 return 1;
385}