- i_img_pal_new() now releases the image object memory if creation
[imager.git] / palimg.c
CommitLineData
faa9b3e7
TC
1/*
2=head1 NAME
3
4 palimg.c - implements paletted images for Imager.
5
6=head1 SYNOPSIS
7
8=head1 DESCRIPTION
9
10Implements paletted images using the new image interface.
11
12=over
13
14=item IIM_base_8bit_pal
15
16Basic 8-bit/sample paletted image
17
18=cut
19*/
20
21#include "image.h"
22#include "imagei.h"
23
24#define PALEXT(im) ((i_img_pal_ext*)((im)->ext_data))
25static int i_ppix_p(i_img *im, int x, int y, i_color *val);
26static int i_gpix_p(i_img *im, int x, int y, i_color *val);
27static int i_glin_p(i_img *im, int l, int r, int y, i_color *vals);
28static int i_plin_p(i_img *im, int l, int r, int y, i_color *vals);
18accb2a 29static int i_gsamp_p(i_img *im, int l, int r, int y, i_sample_t *samps, int const *chans, int chan_count);
faa9b3e7
TC
30static int i_gpal_p(i_img *pm, int l, int r, int y, i_palidx *vals);
31static int i_ppal_p(i_img *pm, int l, int r, int y, i_palidx *vals);
32static int i_addcolors_p(i_img *im, i_color *color, int count);
33static int i_getcolors_p(i_img *im, int i, i_color *color, int count);
34static int i_colorcount_p(i_img *im);
35static int i_maxcolors_p(i_img *im);
36static int i_findcolor_p(i_img *im, i_color *color, i_palidx *entry);
37static int i_setcolors_p(i_img *im, int index, i_color *color, int count);
38
39static void i_destroy_p(i_img *im);
40
41static i_img IIM_base_8bit_pal =
42{
43 0, /* channels set */
44 0, 0, 0, /* xsize, ysize, bytes */
9a88a5e6 45 ~0U, /* ch_mask */
faa9b3e7
TC
46 i_8_bits, /* bits */
47 i_palette_type, /* type */
48 0, /* virtual */
49 NULL, /* idata */
50 { 0, 0, NULL }, /* tags */
51 NULL, /* ext_data */
52
53 i_ppix_p, /* i_f_ppix */
54 i_ppixf_fp, /* i_f_ppixf */
55 i_plin_p, /* i_f_plin */
56 i_plinf_fp, /* i_f_plinf */
57 i_gpix_p, /* i_f_gpix */
58 i_gpixf_fp, /* i_f_gpixf */
59 i_glin_p, /* i_f_glin */
60 i_glinf_fp, /* i_f_glinf */
61 i_gsamp_p, /* i_f_gsamp */
62 i_gsampf_fp, /* i_f_gsampf */
63
64 i_gpal_p, /* i_f_gpal */
65 i_ppal_p, /* i_f_ppal */
66 i_addcolors_p, /* i_f_addcolors */
67 i_getcolors_p, /* i_f_getcolors */
68 i_colorcount_p, /* i_f_colorcount */
69 i_maxcolors_p, /* i_f_maxcolors */
70 i_findcolor_p, /* i_f_findcolor */
71 i_setcolors_p, /* i_f_setcolors */
72
73 i_destroy_p, /* i_f_destroy */
74};
75
76/*
77=item i_img_pal_new_low(i_img *im, int x, int y, int channels, int maxpal)
78
79Creates a new paletted image.
80
81Currently 0 < maxpal <= 256
82
83=cut
84*/
85i_img *i_img_pal_new_low(i_img *im, int x, int y, int channels, int maxpal) {
86 i_img_pal_ext *palext;
653ea321 87 int bytes;
faa9b3e7
TC
88
89 i_clear_error();
90 if (maxpal < 0 || maxpal > 256) {
91 i_push_error(0, "Maximum of 256 palette entries");
92 return NULL;
93 }
94 if (x < 1 || y < 1) {
95 i_push_error(0, "Image sizes must be positive");
96 return NULL;
97 }
98 if (channels < 1 || channels > MAXCHANNELS) {
1501d9b3 99 i_push_errorf(0, "Channels must be positive and <= %d", MAXCHANNELS);
faa9b3e7
TC
100 return NULL;
101 }
653ea321
TC
102 bytes = sizeof(i_palidx) * x * y;
103 if (bytes / y / sizeof(i_palidx) != x) {
104 i_push_errorf(0, "integer overflow calculating image allocation");
105 return NULL;
106 }
faa9b3e7
TC
107
108 memcpy(im, &IIM_base_8bit_pal, sizeof(i_img));
109 palext = mymalloc(sizeof(i_img_pal_ext));
110 palext->pal = mymalloc(sizeof(i_color) * maxpal);
111 palext->count = 0;
112 palext->alloc = maxpal;
113 palext->last_found = -1;
114 im->ext_data = palext;
115 i_tags_new(&im->tags);
653ea321 116 im->bytes = bytes;
faa9b3e7
TC
117 im->idata = mymalloc(im->bytes);
118 im->channels = channels;
705fd961 119 memset(im->idata, 0, im->bytes);
faa9b3e7
TC
120 im->xsize = x;
121 im->ysize = y;
122
123 return im;
124}
125
126i_img *i_img_pal_new(int x, int y, int channels, int maxpal) {
7f882a01
AMH
127 i_img *im;
128 mm_log((1, "i_img_pal_new(x %d, y %d, channels %d, maxpal %d)\n", x, y, channels, maxpal));
129 im = mymalloc(sizeof(i_img));
4dc7c46b
TC
130 if (!i_img_pal_new_low(im, x, y, channels, maxpal)) {
131 myfree(im);
132 im = NULL;
133 }
134
135 return im;
faa9b3e7
TC
136}
137
138/*
139=item i_img_rgb_convert(i_img *targ, i_img *src)
140
141Converts paletted data in src to RGB data in targ
142
143Internal function.
144
145src must be a paletted image and targ must be an RGB image with the
146same width, height and channels.
147
148=cut
149*/
150static void i_img_rgb_convert(i_img *targ, i_img *src) {
151 i_color *row = mymalloc(sizeof(i_color) * targ->xsize);
152 int y;
153 for (y = 0; y < targ->ysize; ++y) {
154 i_glin(src, 0, src->xsize, y, row);
155 i_plin(targ, 0, src->xsize, y, row);
156 }
157 myfree(row);
158}
159
160/*
161=item i_img_to_rgb_inplace(im)
162
163Converts im from a paletted image to an RGB image.
164
165The conversion is done in place.
166
167The conversion cannot be done for virtual images.
168
169=cut
170*/
171int i_img_to_rgb_inplace(i_img *im) {
172 i_img temp;
173 i_color *pal;
174 int palsize;
175
176 if (im->virtual)
177 return 0;
178
179 if (im->type == i_direct_type)
180 return 1; /* trivial success */
181
182 i_img_empty_ch(&temp, im->xsize, im->ysize, im->channels);
183 i_img_rgb_convert(&temp, im);
184
185 /* nasty hack */
186 (im->i_f_destroy)(im);
187 myfree(im->idata);
188 *im = temp;
189
190 return 1;
191}
192
193/*
194=item i_img_to_pal(i_img *im, i_quantize *quant)
195
196Converts an RGB image to a paletted image
197
198=cut
199*/
200i_img *i_img_to_pal(i_img *src, i_quantize *quant) {
201 i_palidx *result;
202 i_img *im;
faa9b3e7 203
1501d9b3
TC
204 i_clear_error();
205
faa9b3e7
TC
206 quant_makemap(quant, &src, 1);
207 result = quant_translate(quant, src);
208
1501d9b3 209 if (result) {
faa9b3e7 210
1501d9b3 211 im = i_img_pal_new(src->xsize, src->ysize, src->channels, quant->mc_size);
faa9b3e7 212
1501d9b3
TC
213 /* copy things over */
214 memcpy(im->idata, result, im->bytes);
215 PALEXT(im)->count = quant->mc_count;
216 memcpy(PALEXT(im)->pal, quant->mc_colors, sizeof(i_color) * quant->mc_count);
217
218 myfree(result);
219
220 return im;
221 }
222 else {
223 return NULL;
224 }
faa9b3e7
TC
225}
226
227/*
228=item i_img_to_rgb(i_img *src)
229
230=cut
231*/
232i_img *i_img_to_rgb(i_img *src) {
233 i_img *im = i_img_empty_ch(NULL, src->xsize, src->ysize, src->channels);
234 i_img_rgb_convert(im, src);
235
236 return im;
237}
238
239/*
240=item i_destroy_p(i_img *im)
241
242Destroys data related to a paletted image.
243
244=cut
245*/
246static void i_destroy_p(i_img *im) {
247 if (im) {
248 i_img_pal_ext *palext = im->ext_data;
249 if (palext) {
250 if (palext->pal)
251 myfree(palext->pal);
252 myfree(palext);
253 }
254 }
255}
256
257/*
258=item i_ppix_p(i_img *im, int x, int y, i_color *val)
259
260Write to a pixel in the image.
261
262Warning: converts the image to a RGB image if the color isn't already
263present in the image.
264
265=cut
266*/
63b018fd 267static int i_ppix_p(i_img *im, int x, int y, i_color *val) {
faa9b3e7
TC
268 i_palidx which;
269 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize)
270 return -1;
271 if (i_findcolor(im, val, &which)) {
272 ((i_palidx *)im->idata)[x + y * im->xsize] = which;
273 return 0;
274 }
275 else {
276 if (i_img_to_rgb_inplace(im)) {
277 return i_ppix(im, x, y, val);
278 }
279 else
280 return -1;
281 }
282}
283
284/*
285=item i_gpix(i_img *im, int x, int y, i_color *val)
286
287Retrieve a pixel, converting from a palette index to a color.
288
289=cut
290*/
63b018fd 291static int i_gpix_p(i_img *im, int x, int y, i_color *val) {
faa9b3e7
TC
292 i_palidx which;
293 if (x < 0 || x >= im->xsize || y < 0 || y >= im->ysize) {
294 return -1;
295 }
296 which = ((i_palidx *)im->idata)[x + y * im->xsize];
297 if (which > PALEXT(im)->count)
298 return -1;
299 *val = PALEXT(im)->pal[which];
300
301 return 0;
302}
303
304/*
305=item i_glinp(i_img *im, int l, int r, int y, i_color *vals)
306
307Retrieve a row of pixels.
308
309=cut
310*/
63b018fd 311static int i_glin_p(i_img *im, int l, int r, int y, i_color *vals) {
faa9b3e7
TC
312 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
313 int palsize = PALEXT(im)->count;
314 i_color *pal = PALEXT(im)->pal;
315 i_palidx *data;
316 int count, i;
317 if (r > im->xsize)
318 r = im->xsize;
319 data = ((i_palidx *)im->idata) + l + y * im->xsize;
320 count = r - l;
321 for (i = 0; i < count; ++i) {
322 i_palidx which = *data++;
323 if (which < palsize)
324 vals[i] = pal[which];
325 }
326 return count;
327 }
328 else {
329 return 0;
330 }
331}
332
333/*
334=item i_plin_p(i_img *im, int l, int r, int y, i_color *vals)
335
336Write a line of color data to the image.
337
338If any color value is not in the image when the image is converted to
339RGB.
340
341=cut
342*/
63b018fd 343static int i_plin_p(i_img *im, int l, int r, int y, i_color *vals) {
faa9b3e7
TC
344 int ch, count, i;
345 i_palidx *data;
346 i_palidx which;
347 if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
348 if (r > im->xsize)
349 r = im->xsize;
350 data = ((i_palidx *)im->idata) + l + y * im->xsize;
351 count = r - l;
352 for (i = 0; i < count; ++i) {
353 if (i_findcolor(im, vals+i, &which)) {
354 ((i_palidx *)data)[i] = which;
355 }
356 else {
357 if (i_img_to_rgb_inplace(im)) {
358 return i+i_plin(im, l+i, r, y, vals+i);
359 }
360 }
361 }
362 return count;
363 }
364 else {
365 return 0;
366 }
367}
368
369/*
370=item i_gsamp_p(i_img *im, int l, int r, int y, i_sample_t *samps, int chans, int chan_count)
371
372=cut
373*/
63b018fd 374static int i_gsamp_p(i_img *im, int l, int r, int y, i_sample_t *samps,
18accb2a 375 int const *chans, int chan_count) {
faa9b3e7
TC
376 int ch;
377 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
378 int palsize = PALEXT(im)->count;
379 i_color *pal = PALEXT(im)->pal;
380 i_palidx *data;
381 int count, i, w;
382 if (r > im->xsize)
383 r = im->xsize;
384 data = ((i_palidx *)im->idata) + l + y * im->xsize;
385 count = 0;
386 w = r - l;
387 if (chans) {
388 for (ch = 0; ch < chan_count; ++ch) {
389 if (chans[ch] < 0 || chans[ch] >= im->channels) {
390 i_push_errorf(0, "No channel %d in this image", chans[ch]);
391 }
392 }
393
394 for (i = 0; i < w; ++i) {
395 i_palidx which = *data++;
396 if (which < palsize) {
397 for (ch = 0; ch < chan_count; ++ch) {
398 *samps++ = pal[which].channel[chans[ch]];
399 ++count;
400 }
401 }
402 }
403 }
404 else {
405 for (i = 0; i < w; ++i) {
406 i_palidx which = *data++;
407 if (which < palsize) {
408 for (ch = 0; ch < chan_count; ++ch) {
409 *samps++ = pal[which].channel[ch];
410 ++count;
411 }
412 }
413 }
414 }
415 return count;
416 }
417 else {
418 return 0;
419 }
420}
421
422/*
423=item i_gpal_p(i_img *im, int l, int r, int y, i_palidx *vals)
424
425=cut
426*/
427
63b018fd 428static int i_gpal_p(i_img *im, int l, int r, int y, i_palidx *vals) {
faa9b3e7
TC
429 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
430 i_palidx *data;
431 int i, w;
432 if (r > im->xsize)
433 r = im->xsize;
434 data = ((i_palidx *)im->idata) + l + y * im->xsize;
435 w = r - l;
436 for (i = 0; i < w; ++i) {
437 *vals++ = *data++;
438 }
439 return i;
440 }
441 else {
442 return 0;
443 }
444}
445
446/*
447=item i_ppal_p(i_img *im, int l, int r, int y, i_palidx *vals)
448
449=cut
450*/
451
63b018fd 452static int i_ppal_p(i_img *im, int l, int r, int y, i_palidx *vals) {
faa9b3e7
TC
453 if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
454 i_palidx *data;
455 int i, w;
456 if (r > im->xsize)
457 r = im->xsize;
458 data = ((i_palidx *)im->idata) + l + y * im->xsize;
459 w = r - l;
460 for (i = 0; i < w; ++i) {
461 *data++ = *vals++;
462 }
463 return i;
464 }
465 else {
466 return 0;
467 }
468}
469
470/*
471=item i_addcolors_p(i_img *im, i_color *color, int count)
472
473=cut
474*/
63b018fd 475static int i_addcolors_p(i_img *im, i_color *color, int count) {
faa9b3e7
TC
476 if (PALEXT(im)->count + count <= PALEXT(im)->alloc) {
477 int result = PALEXT(im)->count;
478 int index = result;
479
480 PALEXT(im)->count += count;
481 while (count) {
482 PALEXT(im)->pal[index++] = *color++;
483 --count;
484 }
485
486 return result;
487 }
488 else
489 return -1;
490}
491
492/*
493=item i_getcolors_p(i_img *im, int i, i_color *color, int count)
494
495=cut
496*/
63b018fd 497static int i_getcolors_p(i_img *im, int i, i_color *color, int count) {
faa9b3e7
TC
498 if (i >= 0 && i+count <= PALEXT(im)->count) {
499 while (count) {
500 *color++ = PALEXT(im)->pal[i++];
501 --count;
502 }
503 return 1;
504 }
505 else
506 return 0;
507}
508
509static int color_eq(i_img *im, i_color *c1, i_color *c2) {
510 int ch;
511 for (ch = 0; ch < im->channels; ++ch) {
512 if (c1->channel[ch] != c2->channel[ch])
513 return 0;
514 }
515 return 1;
516}
517
518/*
519=item i_colorcount_p(i_img *im)
520
521=cut
522*/
63b018fd 523static int i_colorcount_p(i_img *im) {
faa9b3e7
TC
524 return PALEXT(im)->count;
525}
526
527/*
528=item i_maxcolors_p(i_img *im)
529
530=cut
531*/
63b018fd 532static int i_maxcolors_p(i_img *im) {
faa9b3e7
TC
533 return PALEXT(im)->alloc;
534}
535
536/*
537=item i_setcolors_p(i_img *im, int index, i_color *colors, int count)
538
539=cut
540*/
63b018fd 541static int i_setcolors_p(i_img *im, int index, i_color *colors, int count) {
faa9b3e7
TC
542 if (index >= 0 && count >= 1 && index + count < PALEXT(im)->count) {
543 while (count) {
544 PALEXT(im)->pal[index++] = *colors++;
545 --count;
546 }
547 return 1;
548 }
549
550 return 0;
551}
552
553/*
554=item i_findcolor_p(i_img *im)
555
556=cut
557*/
63b018fd 558static int i_findcolor_p(i_img *im, i_color *color, i_palidx *entry) {
faa9b3e7
TC
559 if (PALEXT(im)->count) {
560 int i;
561 /* often the same color comes up several times in a row */
562 if (PALEXT(im)->last_found >= 0) {
563 if (color_eq(im, color, PALEXT(im)->pal + PALEXT(im)->last_found)) {
564 *entry = PALEXT(im)->last_found;
565 return 1;
566 }
567 }
568 for (i = 0; i < PALEXT(im)->count; ++i) {
569 if (color_eq(im, color, PALEXT(im)->pal + i)) {
570 PALEXT(im)->last_found = *entry = i;
571 return 1;
572 }
573 }
574 }
575 return 0;
576}
b8c2033e
AMH
577
578/*
579=back
580
581=head1 AUTHOR
582
583Tony Cook <tony@develop-help.com>
584
585=head1 SEE ALSO
586
587Imager(3)
588
589=cut
590*/