1 #include "declarations.h"
2
3 #if defined(PROG_HAS_LIBRSVG) && \
4 LIBRSVG_MAJOR_VERSION > 2 || (LIBRSVG_MAJOR_VERSION == 2 && LIBRSVG_MINOR_VERSION >= 36)
5 #include <glib.h>
6 #include <librsvg/rsvg.h>
7 #endif
8
9 #include "image.h"
10 #include "config.mach"
11
12 /**
13 * @brief Read BMP image expecting 24 or 32 bits per pixel.
14 *
15 * @param[in] s File name to read
16 * @param[in] n Zero to print error message if file does not exist
17 *
18 * @retval Image structure on success
19 * @retval NULL on failure
20 *
21 */
22
image_bmp_read(char * s,unsigned int n)23 struct t_img *image_bmp_read(char *s, unsigned int n) {
24 unsigned int f;
25
26 struct t_img *r;
27
28 if((f = files_open(s, n, FILE_MODE_READ)) == 0) return(NULL);
29 if((r = image_read_file(f)) == NULL) return(NULL);
30
31 if(image_bmp_read_op(r) != 0) {
32 (void) files_close(f);
33 (void) image_free(r);
34
35 return(NULL);
36 }
37
38 (void) files_close(f);
39
40 return(r);
41 }
42
image_bmp_read_op(struct t_img * r)43 static int image_bmp_read_op(struct t_img *r) {
44 int32_t w, h;
45 uint16_t p;
46 uint32_t c, v;
47
48 size_t t;
49
50 struct bmp_header *d;
51
52 d = (struct bmp_header *) r->c;
53
54 if(d->bf_type != endian_le_uint16(IMAGE_BMP_TAG)) {
55 (void) flush_error();
56
57 LOGWARN(
58 ERROR_SLIGHT, SUBSYSTEM,
59 _("Image identification tag is not what was expected")
60 );
61
62 return(-1);
63 }
64
65 c = endian_le_uint32(d->bf_offbits);
66
67 w = endian_le_int32(d->i.bi_width);
68 h = endian_le_int32(d->i.bi_height);
69 v = endian_le_uint32(d->i.bi_compression);
70 p = endian_le_uint16(d->i.bi_bitcount);
71
72 /* Check that image type is supported */
73 if((v != 0 && v != 3) && (p != 24 && p != 32)) {
74 (void) flush_error();
75
76 LOGWARN(
77 ERROR_SLIGHT, SUBSYSTEM,
78 _("Image type is not expected uncompressed truecolor")
79 );
80
81 return(-1);
82 }
83
84 /* All good, get the pixels */
85 r->w = (int32_t) w;
86 r->h = (int32_t) abs((int) h);
87 r->b = (int32_t) p;
88
89 t = (size_t) ((r->w * r->h) * sizeof(struct pixel_rgba_8));
90
91 APP_MALLOC_RET_VALUE(r->p, t, -1);
92
93 (void) image_bmp_read_it(r, c, p, h);
94
95 return(0);
96 }
97
image_bmp_read_it(struct t_img * r,uint32_t c,uint16_t b,int32_t y)98 static void image_bmp_read_it(struct t_img *r, uint32_t c, uint16_t b, int32_t y) {
99 uint32_t i, j, k, u;
100
101 size_t t;
102
103 char *e;
104
105 struct pixel_bgr_8 *o;
106 struct pixel_abgr_8 *p;
107
108 e = (char *) r->c + c;
109 t = (size_t) c;
110
111 switch(b) {
112 case 24:
113 if(y < 0) {
114 /* Add alpha byte */
115 for(i = 0; i < (uint32_t) r->h; i++) {
116 if(t > r->t) break;
117
118 o = (struct pixel_bgr_8 *) e + (i * ((r->w + 3) / 4 * 4));
119 u = i * r->w;
120
121 for(j = 0; j < (uint32_t) r->w; j++) {
122 if(t > r->t) break;
123
124 (void) image_read_it_bgr_24(r->p, o, u + j, j);
125
126 t += sizeof(struct pixel_bgr_8);
127 }
128 }
129 }
130 else {
131 /* Add alpha byte and turn image upside down */
132 for(i = (uint32_t) r->h, k = 0; i != 0; i--, k++) {
133 if(t > r->t) break;
134
135 o = (struct pixel_bgr_8 *) e + (i * ((r->w + 3) / 4 * 4));
136 u = k * r->w;
137
138 for(j = 0; j < (uint32_t) r->w; j++) {
139 if(t > r->t) break;
140
141 (void) image_read_it_bgr_24(r->p, o, u + j, j);
142
143 t += sizeof(struct pixel_bgr_8);
144 }
145 }
146 }
147
148 break;
149 default:
150 if(y < 0) {
151 /* Swap alpha byte position */
152 for(i = 0; i < (uint32_t) r->h; i++) {
153 if(t > r->t) break;
154
155 p = (struct pixel_abgr_8 *) e + (i * r->w);
156 u = i * r->w;
157
158 for(j = 0; j < (uint32_t) r->w; j++) {
159 if(t > r->t) break;
160
161 (void) image_read_it_abgr_32(r->p, p, u + j, j);
162
163 t += sizeof(struct pixel_abgr_8);
164 }
165 }
166 }
167 else {
168 /* Swap alpha byte position and turn image upside down */
169 for(i = (uint32_t) r->h, k = 0; i != 0; i--, k++) {
170 if(t > r->t) break;
171
172 p = (struct pixel_abgr_8 *) e + (i * r->w);
173 u = k * r->w;
174
175 for(j = 0; j < (uint32_t) r->w; j++) {
176 if(t > r->t) break;
177
178 (void) image_read_it_abgr_32(r->p, p, u + j, j);
179
180 t += sizeof(struct pixel_abgr_8);
181 }
182 }
183 }
184
185 break;
186 }
187 }
188
189 /**
190 * @brief Read SVG image.
191 *
192 * @param[in] s File name to read
193 * @param[in] n Zero to print error message if file does not exist
194 *
195 * @retval Image structure on success
196 * @retval NULL on failure
197 *
198 */
199
image_svg_read(char * s,unsigned int n)200 struct t_img *image_svg_read(char *s, unsigned int n) {
201 #if defined(PROG_HAS_LIBRSVG) && \
202 LIBRSVG_MAJOR_VERSION > 2 || (LIBRSVG_MAJOR_VERSION == 2 && LIBRSVG_MINOR_VERSION >= 36)
203 GdkPixbuf *p;
204 GError *e;
205
206 RsvgDimensionData d;
207 RsvgHandle *r;
208
209 cairo_t *c;
210 cairo_surface_t *f;
211
212 struct stat a;
213
214 struct t_img *m;
215
216 /* Check if file exists */
217 if(stat((const char *) s, &a) != 0) {
218 if(n == 0) {
219 LOGWARN(
220 ERROR_SLIGHT, SUBSYSTEM,
221 _("Failed to open file '%s'"),
222 s
223 );
224 }
225
226 return(NULL);
227 }
228
229 /* Render the file */
230 e = NULL;
231
232 if((r = rsvg_handle_new_from_file((const gchar *) s, &e)) == NULL) {
233 if(e != NULL) {
234 (void) flush_error();
235
236 LOGWARN(
237 ERROR_SLIGHT, SUBSYSTEM,
238 _("Failed to open file '%s': %s"),
239 e->message
240 );
241 }
242
243 return(NULL);
244 }
245
246 (void) rsvg_handle_set_dpi(r, 300.0);
247 (void) rsvg_handle_get_dimensions(r, &d);
248
249 f = cairo_image_surface_create(CAIRO_FORMAT_RGB24, (int) d.width, (int) d.height);
250
251 if(cairo_surface_status(f) != CAIRO_STATUS_SUCCESS) {
252 (void) flush_error();
253
254 LOGWARN(
255 ERROR_SLIGHT, SUBSYSTEM,
256 _("Failed to create cairo surface: %s"),
257 (char *) cairo_status_to_string(cairo_surface_status(f))
258 );
259
260 (void) image_svg_read_ca(r);
261
262 return(NULL);
263 }
264
265 c = cairo_create(f);
266
267 if(cairo_status(c) != CAIRO_STATUS_SUCCESS) {
268 (void) flush_error();
269
270 LOGWARN(
271 ERROR_SLIGHT, SUBSYSTEM,
272 _("Failed to create cairo context: %s"),
273 (char *) cairo_status_to_string(cairo_status(c))
274 );
275
276 (void) cairo_surface_destroy(f);
277
278 (void) image_svg_read_ca(r);
279
280 return(NULL);
281 }
282
283 (void) cairo_surface_destroy(f);
284 #if defined(CAIRO_ANTIALIAS_BEST)
285 (void) cairo_set_antialias(c, CAIRO_ANTIALIAS_BEST);
286 #else
287 (void) cairo_set_antialias(c, CAIRO_ANTIALIAS_DEFAULT);
288 #endif
289 if(rsvg_handle_render_cairo(r, c) != TRUE) {
290 (void) flush_error();
291
292 LOGWARN(
293 ERROR_SLIGHT, SUBSYSTEM,
294 _("Failed to render to cairo context")
295 );
296
297 (void) image_svg_read_cb(r, c);
298
299 return(NULL);
300 }
301
302 if((p = rsvg_handle_get_pixbuf(r)) == NULL) {
303 (void) flush_error();
304
305 LOGWARN(
306 ERROR_SLIGHT, SUBSYSTEM,
307 _("Failed to get the pixbuf")
308 );
309
310 (void) image_svg_read_cb(r, c);
311
312 return(NULL);
313 }
314
315 (void) rsvg_handle_close(r, &e);
316
317 /* Copy SVG pixbuf to own buffer */
318 if((m = malloc(sizeof(struct t_img))) == NULL) {
319 LOGWARN(
320 ERROR_SLIGHT, SUBSYSTEM,
321 _("Failed to allocate %lu bytes of memory"),
322 (unsigned long) sizeof(struct t_img)
323 );
324 }
325
326 if(m != NULL) {
327 if(image_svg_read_op(m, p) != 0) {
328 (void) image_free(m);
329
330 m = NULL;
331 }
332 }
333
334 (void) cairo_destroy(c);
335
336 (void) g_object_unref((gpointer) r);
337 (void) g_object_unref((gpointer) p);
338
339 return(m);
340 #else
341 (void) s;
342 (void) n;
343
344 return(NULL);
345 #endif
346 }
347 #if defined(PROG_HAS_LIBRSVG) && \
348 LIBRSVG_MAJOR_VERSION > 2 || (LIBRSVG_MAJOR_VERSION == 2 && LIBRSVG_MINOR_VERSION >= 36)
image_svg_read_ca(RsvgHandle * r)349 static void image_svg_read_ca(RsvgHandle *r) {
350 GError *e;
351
352 e = NULL;
353
354 (void) rsvg_handle_close(r, &e);
355
356 (void) g_object_unref((gpointer) r);
357 }
358
image_svg_read_cb(RsvgHandle * r,cairo_t * c)359 static void image_svg_read_cb(RsvgHandle *r, cairo_t *c) {
360 (void) cairo_destroy(c);
361
362 (void) image_svg_read_ca(r);
363 }
364
image_svg_read_op(struct t_img * m,GdkPixbuf * p)365 static int image_svg_read_op(struct t_img *m, GdkPixbuf *p) {
366 int k, n;
367
368 int32_t i, j;
369 uint32_t o;
370
371 size_t t;
372
373 const guchar *d, *e, *f;
374
375 m->c = NULL;
376 m->t = 0;
377
378 m->w = (int32_t) gdk_pixbuf_get_width(p);
379 m->h = (int32_t) gdk_pixbuf_get_height(p);
380 m->b = (int32_t) gdk_pixbuf_get_bits_per_sample(p);
381
382 k = gdk_pixbuf_get_rowstride(p);
383 n = gdk_pixbuf_get_n_channels(p);
384
385 t = (size_t) (m->w * m->h * sizeof(struct pixel_rgba_8));
386
387 APP_MALLOC_RET_VALUE(m->p, t, -1);
388
389 d = gdk_pixbuf_read_pixels(p);
390
391 for(i = 0; i < m->h; i++) {
392 e = d + (i * k);
393
394 for(j = 0; j < m->w; j++) {
395 f = e + (j * n);
396 o = (i * m->w) + j;
397
398 m->p[o].pixel.r = f[0];
399 m->p[o].pixel.g = f[1];
400 m->p[o].pixel.b = f[2];
401 m->p[o].pixel.a = f[3];
402 }
403 }
404
405 return(0);
406 }
407 #endif
408
409 /**
410 * @brief Read Truevision TGA image expecting 24 or 32 bits per pixel.
411 *
412 * @param[in] s File name to read
413 * @param[in] n Zero to print error message if file does not exist
414 *
415 * @retval Image structure on success
416 * @retval NULL on failure
417 *
418 */
419
image_tga_read(char * s,unsigned int n)420 struct t_img *image_tga_read(char *s, unsigned int n) {
421 unsigned int f;
422
423 struct t_img *r;
424
425 if((f = files_open(s, n, FILE_MODE_READ)) == 0) return(NULL);
426 if((r = image_read_file(f)) == NULL) return(NULL);
427
428 if(image_tga_read_op(r) != 0) {
429 (void) files_close(f);
430 (void) image_free(r);
431
432 return(NULL);
433 }
434
435 (void) files_close(f);
436
437 return(r);
438 }
439
image_tga_read_op(struct t_img * r)440 static int image_tga_read_op(struct t_img *r) {
441 uint8_t c, m, p, v;
442 uint16_t n, w, h;
443
444 size_t t;
445
446 struct tga_header *d;
447
448 d = (struct tga_header *) r->c;
449
450 c = endian_le_uint8(d->d);
451 m = endian_le_uint8(d->m);
452 v = endian_le_uint8(d->t);
453
454 n = endian_le_uint16(d->c.n);
455
456 w = endian_le_uint16(d->i.w);
457 h = endian_le_uint16(d->i.h);
458 p = endian_le_uint8(d->i.b);
459
460 /* Check that image type is supported */
461 if((m != 0 || v != IMAGE_TGA_STORAGE_VERBATIM || n != 0) && (p != 24 && p != 32)) {
462 (void) flush_error();
463
464 LOGWARN(
465 ERROR_SLIGHT, SUBSYSTEM,
466 _("Image type is not expected uncompressed truecolor")
467 );
468
469 return(-1);
470 }
471
472 /* All good, get the pixels */
473 r->w = (int32_t) w;
474 r->h = (int32_t) h;
475 r->b = (int32_t) p;
476
477 t = (size_t) ((r->w * r->h) * sizeof(struct pixel_rgba_8));
478
479 APP_MALLOC_RET_VALUE(r->p, t, -1);
480
481 (void) image_tga_read_it(r, c, p, endian_le_int16(d->i.y));
482
483 return(0);
484 }
485
image_tga_read_it(struct t_img * r,uint8_t c,uint8_t b,int16_t y)486 static void image_tga_read_it(struct t_img *r, uint8_t c, uint8_t b, int16_t y) {
487 uint32_t i, j, k, l, u, v;
488
489 size_t t;
490
491 char *e;
492
493 struct pixel_bgr_8 *o;
494 struct pixel_bgra_8 *p;
495
496 e = (char *) r->c + sizeof(struct tga_header) + c;
497 t = (size_t) (sizeof(struct tga_header) + c);
498
499 switch(b) {
500 case 24:
501 o = (struct pixel_bgr_8 *) e;
502
503 if(y == 0) {
504 /* Add alpha byte and turn image upside down */
505 for(i = (uint32_t) r->h, k = 0; i != 0; i--, k++) {
506 if(t > r->t) break;
507
508 u = (i - 1) * r->w;
509 v = k * r->w;
510
511 for(j = 0, l = u; j < (uint32_t) r->w; j++, l++) {
512 if(t > r->t) break;
513
514 (void) image_read_it_bgr_24(r->p, o, v + j, l);
515
516 t += sizeof(struct pixel_bgr_8);
517 }
518 }
519 }
520 else {
521 /* Add alpha byte */
522 for(i = 0; i < (uint32_t) r->h; i++) {
523 if(t > r->t) break;
524
525 u = i * r->w;
526
527 for(j = 0; j < (uint32_t) r->w; j++) {
528 if(t > r->t) break;
529
530 (void) image_read_it_bgr_24(r->p, o, u + j, u + j);
531
532 t += sizeof(struct pixel_bgr_8);
533 }
534 }
535 }
536
537 break;
538 default:
539 p = (struct pixel_bgra_8 *) e;
540
541 if(y == 0) {
542 /* Swap alpha byte position and turn image upside down */
543 for(i = (uint32_t) r->h, k = 0; i != 0; i--, k++) {
544 if(t > r->t) break;
545
546 u = (i - 1) * r->w;
547 v = k * r->w;
548
549 for(j = 0, l = u; j < (uint32_t) r->w; j++, l++) {
550 if(t > r->t) break;
551
552 (void) image_read_it_bgra_32(r->p, p, v + j, l);
553
554 t += sizeof(struct pixel_bgra_8);
555 }
556 }
557 }
558 else {
559 /* Swap alpha byte position */
560 for(i = 0; i < (uint32_t) r->h; i++) {
561 if(t > r->t) break;
562
563 u = i * r->w;
564
565 for(j = 0; j < (uint32_t) r->w; j++) {
566 if(t > r->t) break;
567
568 (void) image_read_it_bgra_32(r->p, p, u + j, u + j);
569
570 t += sizeof(struct pixel_bgra_8);
571 }
572 }
573 }
574
575 break;
576 }
577 }
578
579 /*
580 static void image_read_it_bgr_15(struct pixel_rgba_8 *d, struct pixel_ac_1 *s, uint32_t a, uint32_t b) {
581 #if defined(IS_BIGENDIAN)
582 d[a].pixel.r = (s[b].pixel.a >> 2) & 0x1f;
583 d[a].pixel.g = ((s[b].pixel.a << 3) & 0x1c) | ((s[b].pixel.c >> 5) & 0x07);
584 d[a].pixel.b = (s[b].pixel.c & 0x1f);
585 #else
586 d[a].pixel.r = (s[b].pixel.c >> 2) & 0x1f;
587 d[a].pixel.g = ((s[b].pixel.c << 3) & 0x1c) | ((s[b].pixel.a >> 5) & 0x07);
588 d[a].pixel.b = (s[b].pixel.a & 0x1f);
589 #endif
590 d[a].pixel.r = (d[a].pixel.r << 3) | (d[a].pixel.r >> 2);
591 d[a].pixel.g = (d[a].pixel.g << 3) | (d[a].pixel.g >> 2);
592 d[a].pixel.b = (d[a].pixel.b << 3) | (d[a].pixel.b >> 2);
593
594 d[a].pixel.a = 255;
595 }
596
597 static void image_read_it_bgr_16(struct pixel_rgba_8 *d, struct pixel_ac_1 *s, uint32_t a, uint32_t b) {
598 #if defined(IS_BIGENDIAN)
599 d[a].pixel.r = (s[b].pixel.a >> 2) & 0x1f;
600 d[a].pixel.g = ((s[b].pixel.a << 3) & 0x1c) | ((s[b].pixel.c >> 5) & 0x07);
601 d[a].pixel.b = (s[b].pixel.c & 0x1f);
602 d[a].pixel.a = 255 * ((s[b].pixel.a & 0x80) >> 0x07);
603 #else
604 d[a].pixel.r = (s[b].pixel.c >> 2) & 0x1f;
605 d[a].pixel.g = ((s[b].pixel.c << 3) & 0x1c) | ((s[b].pixel.a >> 5) & 0x07);
606 d[a].pixel.b = (s[b].pixel.a & 0x1f);
607 d[a].pixel.a = 255 * ((s[b].pixel.c & 0x80) >> 0x07);
608 #endif
609 d[a].pixel.r = (d[a].pixel.r << 3) | (d[a].pixel.r >> 2);
610 d[a].pixel.g = (d[a].pixel.g << 3) | (d[a].pixel.g >> 2);
611 d[a].pixel.b = (d[a].pixel.b << 3) | (d[a].pixel.b >> 2);
612 }
613 */
614
image_read_it_bgr_24(struct pixel_rgba_8 * d,struct pixel_bgr_8 * s,uint32_t a,uint32_t b)615 static void image_read_it_bgr_24(struct pixel_rgba_8 *d, struct pixel_bgr_8 *s, uint32_t a, uint32_t b) {
616 #if defined(IS_BIGENDIAN)
617 d[a].pixel.r = s[b].r;
618 d[a].pixel.g = s[b].g;
619 d[a].pixel.b = s[b].b;
620 #else
621 d[a].pixel.r = s[b].b;
622 d[a].pixel.g = s[b].g;
623 d[a].pixel.b = s[b].r;
624 #endif
625 d[a].pixel.a = 255;
626 }
627
image_read_it_bgra_32(struct pixel_rgba_8 * d,struct pixel_bgra_8 * s,uint32_t a,uint32_t b)628 static void image_read_it_bgra_32(struct pixel_rgba_8 *d, struct pixel_bgra_8 *s, uint32_t a, uint32_t b) {
629 #if defined(IS_BIGENDIAN)
630 d[a].pixel.b = s[b].pixel.b;
631 d[a].pixel.g = s[b].pixel.g;
632 d[a].pixel.r = s[b].pixel.r;
633 d[a].pixel.a = s[b].pixel.a;
634 #else
635 d[a].pixel.b = s[b].pixel.a;
636 d[a].pixel.g = s[b].pixel.r;
637 d[a].pixel.r = s[b].pixel.g;
638 d[a].pixel.a = s[b].pixel.b;
639 #endif
640 }
641
image_read_it_abgr_32(struct pixel_rgba_8 * d,struct pixel_abgr_8 * s,uint32_t a,uint32_t b)642 static void image_read_it_abgr_32(struct pixel_rgba_8 *d, struct pixel_abgr_8 *s, uint32_t a, uint32_t b) {
643 #if defined(IS_BIGENDIAN)
644 d[a].pixel.a = s[b].pixel.a;
645 d[a].pixel.b = s[b].pixel.b;
646 d[a].pixel.g = s[b].pixel.g;
647 d[a].pixel.r = s[b].pixel.r;
648 #else
649 d[a].pixel.a = s[b].pixel.r;
650 d[a].pixel.b = s[b].pixel.g;
651 d[a].pixel.g = s[b].pixel.b;
652 d[a].pixel.r = s[b].pixel.a;
653 #endif
654 }
655
image_read_file(unsigned int f)656 static struct t_img *image_read_file(unsigned int f) {
657 void *c;
658
659 size_t t;
660
661 struct t_img *r;
662
663 if((t = files_get_file_size(f)) == 0) {
664 (void) files_close(f);
665
666 return(NULL);
667 }
668
669 if((files_read(f, t)) != 0) {
670 (void) files_close(f);
671
672 return(NULL);
673 }
674
675 if((c = files_get_content(f)) == NULL) {
676 (void) files_close(f);
677
678 return(NULL);
679 }
680
681 if((r = malloc(sizeof(struct t_img))) == NULL) {
682 LOGWARN(
683 ERROR_SLIGHT, SUBSYSTEM,
684 _("Failed to allocate %lu bytes of memory"),
685 (unsigned long) sizeof(struct t_img)
686 );
687
688 (void) files_close(f);
689
690 return(NULL);
691 }
692
693 (void) memset((void *) r, 0, sizeof(struct t_img));
694
695 r->p = NULL;
696
697 r->c = c;
698 r->t = t;
699
700 return(r);
701 }
702
703 /**
704 * @brief Read image by format.
705 *
706 * @param[in] s File name to read
707 * @param[in] n Zero to print error message if file does not exist
708 *
709 * @retval Image structure on success
710 * @retval NULL on failure
711 *
712 */
713
image_read(char * s,unsigned int n)714 struct t_img *image_read(char *s, unsigned int n) {
715 char *p;
716
717 if(s == NULL) return(NULL);
718
719 if((p = strrchr((const char *) s, '.')) == NULL) {
720 (void) flush_error();
721
722 LOGWARN(
723 ERROR_SLIGHT, SUBSYSTEM,
724 _("Failed to detect image type from its name")
725 );
726
727 return(NULL);
728 }
729
730 switch(image_get_type_by_suffix(++p)) {
731 case IMAGE_FORMAT_BMP:
732 return(image_bmp_read(s, n));
733 #if defined(PROG_HAS_LIBRSVG) && \
734 LIBRSVG_MAJOR_VERSION > 2 || (LIBRSVG_MAJOR_VERSION == 2 && LIBRSVG_MINOR_VERSION >= 36)
735 case IMAGE_FORMAT_SVG:
736 return(image_svg_read(s, n));
737 #endif
738 case IMAGE_FORMAT_TGA:
739 return(image_tga_read(s, n));
740 default:
741 (void) flush_error();
742
743 LOGWARN(
744 ERROR_SLIGHT, SUBSYSTEM,
745 _("Image type '%s' is not supported"),
746 p
747 );
748
749 break;
750 }
751
752 return(NULL);
753 }
754
755 /**
756 * @brief Free image structure.
757 *
758 * @param[in] r Image structure returned by image_*_read_*()
759 *
760 */
761
image_free(struct t_img * r)762 void image_free(struct t_img *r) {
763 if(r != NULL) {
764 if(r->p != NULL) (void) free(r->p);
765
766 (void) free(r);
767 }
768 }
769
770 /**
771 * @brief Write BMP image using 32 bits (RGBA) per pixel.
772 *
773 * @param[in] p Image pixel data
774 * @param[in] w Image width in pixels
775 * @param[in] h Image height in pixels
776 * @param[in] s File name to write
777 *
778 * @retval Zero on success
779 * @retval Non-zero on failure
780 *
781 */
782
783 #if ! defined(PROG_DISABLE_IMAGE)
image_bmp_write_32(struct pixel_rgba_8 * p,unsigned int w,unsigned int h,char * s)784 int image_bmp_write_32(struct pixel_rgba_8 *p, unsigned int w, unsigned int h, char *s) {
785 unsigned int f;
786
787 if((f = files_open(s, FILE_FLAG_NOCOMPRESS, FILE_MODE_WRITE_AND_TRUNCATE)) == 0) return(-1);
788
789 if(image_bmp_header((int32_t) w, (int32_t) h, 32, f) != 0) return(-1);
790 if(image_bmp_channs(p, (int32_t) w, (int32_t) h, f) != 0) return(-1);
791
792 (void) files_close(f);
793
794 return(0);
795 }
796
image_bmp_header(int32_t w,int32_t h,uint16_t b,unsigned int f)797 static int image_bmp_header(int32_t w, int32_t h, uint16_t b, unsigned int f) {
798 struct bmp_header d;
799
800 (void) memset((void *) &d, 0, sizeof(d));
801
802 d.bf_type = endian_le_uint16(IMAGE_BMP_TAG);
803
804 d.bf_size = endian_le_uint32((uint32_t) sizeof(struct bmp_header) + (w * h * sizeof(struct pixel_rgba_8)));
805 d.bf_offbits = endian_le_uint32((uint32_t) sizeof(struct bmp_header));
806
807 d.i.bi_size = endian_le_uint32((uint32_t) sizeof(struct bmp_header_info));
808
809 /* Dimensions */
810 d.i.bi_width = endian_le_int32((int32_t) w);
811 d.i.bi_height = endian_le_int32((int32_t) h);
812
813 /* Storage type */
814 d.i.bi_planes = endian_le_uint16(1);
815 d.i.bi_bitcount = endian_le_uint16((uint32_t) b);
816
817 d.i.bi_compression = endian_le_uint32(0);
818 d.i.bi_sizeimage = endian_le_uint32((uint32_t) (w * h * sizeof(struct pixel_rgba_8)));
819 d.i.bi_xpelspermeter = endian_le_uint32(2835);
820 d.i.bi_ypelspermeter = endian_le_uint32(2835);
821
822 /* Colormap */
823 d.i.bi_clrused = endian_le_uint32(0);
824 d.i.bi_clrimportant = endian_le_uint32(0);
825
826 return(files_write(f, (void *) &d, sizeof(d)));
827 }
828
image_bmp_channs(struct pixel_rgba_8 * p,int32_t w,int32_t h,unsigned int f)829 static int image_bmp_channs(struct pixel_rgba_8 *p, int32_t w, int32_t h, unsigned int f) {
830 uint32_t i, j, k, l;
831
832 struct pixel_abgr_8 a;
833
834 /* Swap alpha byte position and turn image upside down */
835 for(i = (uint32_t) h; i != 0; i--) {
836 k = (i - 1) * w;
837
838 for(j = 0; j < (uint32_t) w; j++) {
839 l = k + j;
840 #if defined(IS_BIGENDIAN)
841 a.pixel.a = p[l].pixel.b;
842 a.pixel.b = p[l].pixel.g;
843 a.pixel.g = p[l].pixel.r;
844 a.pixel.r = p[l].pixel.a;
845 #else
846 a.pixel.a = p[l].pixel.a;
847 a.pixel.b = p[l].pixel.r;
848 a.pixel.g = p[l].pixel.g;
849 a.pixel.r = p[l].pixel.b;
850 #endif
851 if(files_write(f, (void *) &a.pixel.p, sizeof(a.pixel.p)) != 0) return(-1);
852 }
853 }
854
855 return(0);
856 }
857 #endif
858
859 /**
860 * @brief Write KISS CEL image using 32 bits (RGBA) per pixel.
861 *
862 * @param[in] p Image pixel data
863 * @param[in] w Image width in pixels
864 * @param[in] h Image height in pixels
865 * @param[in] s File name to write
866 *
867 * @retval Zero on success
868 * @retval Non-zero on failure
869 *
870 */
871
872 #if ! defined(PROG_DISABLE_IMAGE)
image_cel_write_32(struct pixel_rgba_8 * p,unsigned int w,unsigned int h,char * s)873 int image_cel_write_32(struct pixel_rgba_8 *p, unsigned int w, unsigned int h, char *s) {
874 unsigned int f;
875
876 if((f = files_open(s, FILE_FLAG_NOCOMPRESS, FILE_MODE_WRITE_AND_TRUNCATE)) == 0) return(-1);
877
878 if(image_cel_header(IMAGE_CEL_COLOR, (uint16_t) w, (uint16_t) h, 32, f) != 0) return(-1);
879 if(image_cel_channs(p, w, h, f) != 0) return(-1);
880
881 (void) files_close(f);
882
883 return(0);
884 }
885
image_cel_header(int8_t t,uint16_t w,uint16_t h,uint16_t b,unsigned int f)886 static int image_cel_header(int8_t t, uint16_t w, uint16_t h, uint16_t b, unsigned int f) {
887 struct cel_header d;
888
889 (void) memset((void *) &d, 0, sizeof(d));
890
891 d.id = endian_le_int32(IMAGE_CEL_TAG);
892
893 /* Storage type */
894 d.type = endian_le_int8(t);
895 d.bpp = endian_le_int8((int8_t) b);
896
897 /* Dimensions */
898 d.width = endian_le_uint16(w);
899 d.height = endian_le_uint16(h);
900 d.xoffset = endian_le_uint16(0);
901 d.yoffset = endian_le_uint16(0);
902
903 return(files_write(f, (void *) &d, sizeof(d)));
904 }
905
image_cel_channs(struct pixel_rgba_8 * p,uint16_t w,uint16_t h,unsigned int f)906 static int image_cel_channs(struct pixel_rgba_8 *p, uint16_t w, uint16_t h, unsigned int f) {
907 uint32_t i;
908
909 struct pixel_argb_8 a;
910
911 /* Swap alpha byte position */
912 for(i = 0; i < (uint32_t) w * h; i++) {
913 #if defined(IS_BIGENDIAN)
914 a.pixel.a = p[i].pixel.b;
915 a.pixel.r = p[i].pixel.g;
916 a.pixel.g = p[i].pixel.r;
917 a.pixel.b = p[i].pixel.a;
918 #else
919 a.pixel.a = p[i].pixel.a;
920 a.pixel.r = p[i].pixel.r;
921 a.pixel.g = p[i].pixel.g;
922 a.pixel.b = p[i].pixel.b;
923 #endif
924 if(files_write(f, (void *) &a.pixel.p, sizeof(a.pixel.p)) != 0) return(-1);
925 }
926
927 return(0);
928 }
929 #endif
930
931 /**
932 * @brief Write Alias PIX image using 24 bits (RGB) + 8 bits (A) per pixel.
933 *
934 * Alpha bits are stored in separate .matte file.
935 *
936 * @param[in] p Image pixel data
937 * @param[in] w Image width in pixels
938 * @param[in] h Image height in pixels
939 * @param[in] s File name to write
940 *
941 * @retval Zero on success
942 * @retval Non-zero on failure
943 *
944 */
945
946 #if ! defined(PROG_DISABLE_IMAGE)
image_pix_write_32(struct pixel_rgba_8 * p,unsigned int w,unsigned int h,char * s)947 int image_pix_write_32(struct pixel_rgba_8 *p, unsigned int w, unsigned int h, char *s) {
948 unsigned int f;
949
950 char *e;
951
952 size_t t;
953
954 if((f = files_open(s, FILE_FLAG_NOCOMPRESS, FILE_MODE_WRITE_AND_TRUNCATE)) == 0) return(-1);
955
956 if(image_pix_header((uint16_t) w, (uint16_t) h, 24, f) != 0) return(-1);
957 if(image_pix_channs(p, w, h, f) != 0) return(-1);
958
959 (void) files_close(f);
960
961 t = str_len(s, STRING_ASCII) + (sizeof(char) * 8);
962
963 APP_MALLOC_RET_VALUE(e, t, -1);
964
965 (void) snprintf(e, t, "%s.matte", s);
966
967 if((f = files_open(e, FILE_FLAG_NOCOMPRESS, FILE_MODE_WRITE_AND_TRUNCATE)) == 0) {
968 (void) free(e);
969
970 return(-1);
971 }
972
973 if(image_pix_header((uint16_t) w, (uint16_t) h, 8, f) != 0) {
974 (void) free(e);
975
976 return(-1);
977 }
978
979 if(image_pix_mattes(p, w, h, f) != 0) {
980 (void) free(e);
981
982 return(-1);
983 }
984
985 (void) files_close(f);
986 (void) free(e);
987
988 return(0);
989 }
990
image_pix_header(uint16_t w,uint16_t h,uint16_t b,unsigned int f)991 static int image_pix_header(uint16_t w, uint16_t h, uint16_t b, unsigned int f) {
992 struct pix_header d;
993
994 (void) memset((void *) &d, 0, sizeof(d));
995
996 /* Dimensions */
997 d.width = endian_be_uint16(w);
998 d.height = endian_be_uint16(h);
999 d.xoffset = endian_be_uint16(0);
1000 d.yoffset = endian_be_uint16(0);
1001
1002 /* Storage type */
1003 d.bpp = endian_be_uint16(b);
1004
1005 return(files_write(f, (void *) &d, sizeof(d)));
1006 }
1007
image_pix_channs(struct pixel_rgba_8 * p,uint16_t w,uint16_t h,unsigned int f)1008 static int image_pix_channs(struct pixel_rgba_8 *p, uint16_t w, uint16_t h, unsigned int f) {
1009 uint32_t i, j, k, c;
1010
1011 struct pixel_rgba_8 a;
1012
1013 /* PIX style RLE compression */
1014 for(i = 0; i < (uint32_t) h; i++) {
1015 k = i * w;
1016 c = 0;
1017
1018 a.pixel.r = p[k].pixel.r;
1019 a.pixel.g = p[k].pixel.g;
1020 a.pixel.b = p[k].pixel.b;
1021
1022 for(j = 0; j < (uint32_t) w; j++) {
1023 if(c < 255 && (a.pixel.r == p[k + j].pixel.r && a.pixel.g == p[k + j].pixel.g && a.pixel.b == p[k + j].pixel.b)) ++c;
1024 else {
1025 a.pixel.a = (uint8_t) c;
1026 a.pixel.p = endian_le_uint32(a.pixel.p);
1027
1028 if(files_write(f, (void *) &a.pixel.p, sizeof(a.pixel.p)) != 0) return(-1);
1029
1030 c = 1;
1031
1032 a.pixel.r = p[k + j].pixel.r;
1033 a.pixel.g = p[k + j].pixel.g;
1034 a.pixel.b = p[k + j].pixel.b;
1035 }
1036 }
1037
1038 if(c > 0) {
1039 a.pixel.a = (uint8_t) c;
1040 a.pixel.p = endian_le_uint32(a.pixel.p);
1041
1042 if(files_write(f, (void *) &a.pixel.p, sizeof(a.pixel.p)) != 0) return(-1);
1043 }
1044 }
1045
1046 return(0);
1047 }
1048
image_pix_mattes(struct pixel_rgba_8 * p,uint16_t w,uint16_t h,unsigned int f)1049 static int image_pix_mattes(struct pixel_rgba_8 *p, uint16_t w, uint16_t h, unsigned int f) {
1050 uint32_t i, j, k, c;
1051
1052 struct pixel_ca_1 a;
1053
1054 /* PIX style RLE compression */
1055 for(i = 0; i < (uint32_t) h; i++) {
1056 k = i * w;
1057 c = 0;
1058
1059 a.pixel.c = p[k].pixel.a;
1060
1061 for(j = 0; j < (uint32_t) w; j++) {
1062 if(c < 255 && (a.pixel.c == p[k + j].pixel.a)) ++c;
1063 else {
1064 a.pixel.a = (uint8_t) c;
1065 a.pixel.p = endian_le_uint16(a.pixel.p);
1066
1067 if(files_write(f, (void *) &a.pixel.p, sizeof(a.pixel.p)) != 0) return(-1);
1068
1069 c = 1;
1070
1071 a.pixel.c = p[k + j].pixel.a;
1072 }
1073 }
1074
1075 if(c > 0) {
1076 a.pixel.a = (uint8_t) c;
1077 a.pixel.p = endian_le_uint16(a.pixel.p);
1078
1079 if(files_write(f, (void *) &a.pixel.p, sizeof(a.pixel.p)) != 0) return(-1);
1080 }
1081 }
1082
1083 return(0);
1084 }
1085 #endif
1086
1087 /**
1088 * @brief Write NetPBM PPM image using 24 bits (RGB) per pixel.
1089 *
1090 * @param[in] p Image pixel data
1091 * @param[in] w Image width in pixels
1092 * @param[in] h Image height in pixels
1093 * @param[in] s File name to write
1094 *
1095 * @retval Zero on success
1096 * @retval Non-zero on failure
1097 *
1098 */
1099
1100 #if ! defined(PROG_DISABLE_IMAGE)
image_ppm_write_32(struct pixel_rgba_8 * p,unsigned int w,unsigned int h,char * s)1101 int image_ppm_write_32(struct pixel_rgba_8 *p, unsigned int w, unsigned int h, char *s) {
1102 unsigned int f;
1103
1104 if((f = files_open(s, FILE_FLAG_NOCOMPRESS, FILE_MODE_WRITE_AND_TRUNCATE)) == 0) return(-1);
1105
1106 if(image_ppm_header((uint16_t) w, (uint16_t) h, 255, f) != 0) return(-1);
1107 if(image_ppm_channs(p, w, h, f) != 0) return(-1);
1108
1109 (void) files_close(f);
1110
1111 return(0);
1112 }
1113
image_ppm_header(uint16_t w,uint16_t h,uint16_t b,unsigned int f)1114 static int image_ppm_header(uint16_t w, uint16_t h, uint16_t b, unsigned int f) {
1115 char *s, *r, *k;
1116
1117 size_t n;
1118
1119 time_t m;
1120
1121 struct tm *t;
1122
1123 APP_MALLOC_RET_VALUE(r, CONFIG_LINE_LENGTH * 8, -1);
1124
1125 /* Identifier */
1126 n = (size_t) snprintf(r, CONFIG_LINE_LENGTH, "%s%c", IMAGE_PPM_TAG, CONFIG_LINE_FEED);
1127
1128 /* Creator info */
1129 if((s = files_get_name(f)) != NULL) {
1130 if((k = strrchr((const char *) s, CONFIG_PATH_DELIM)) == NULL) k = s;
1131 else k += sizeof(char);
1132 }
1133 else k = "";
1134
1135 n += (size_t) snprintf(r + n, CONFIG_LINE_LENGTH, "# %s%c", k, CONFIG_LINE_FEED);
1136 n += (size_t) snprintf(r + n, CONFIG_LINE_LENGTH, "# " APPLICATION_NAME " " APPLICATION_VERSION " (" CONFIG_MACH ")%c", CONFIG_LINE_FEED);
1137 n += (size_t) snprintf(r + n, CONFIG_LINE_LENGTH, "# %s%c", node_get_node(), CONFIG_LINE_FEED);
1138 n += (size_t) snprintf(r + n, CONFIG_LINE_LENGTH, "# %s%c", node_get_user(), CONFIG_LINE_FEED);
1139
1140 if((m = time(NULL)) != (time_t) -1) {
1141 if((t = localtime(&m)) != NULL) {
1142 n += (size_t) strftime(r + n, CONFIG_LINE_LENGTH, "# %b %d %H:%M %Y%n", t);
1143 }
1144 }
1145
1146 /* Dimensions */
1147 n += (size_t) snprintf(r + n, CONFIG_LINE_LENGTH, "%u %u%c", (unsigned int) w, (unsigned int) h, CONFIG_LINE_FEED);
1148 n += (size_t) snprintf(r + n, CONFIG_LINE_LENGTH, "%u%c", (unsigned int) b, CONFIG_LINE_FEED);
1149
1150 if(files_write(f, (void *) r, n) != 0) {
1151 (void) free(r);
1152
1153 return(-1);
1154 }
1155
1156 (void) free(r);
1157
1158 return(0);
1159 }
1160
image_ppm_channs(struct pixel_rgba_8 * p,uint16_t w,uint16_t h,unsigned int f)1161 static int image_ppm_channs(struct pixel_rgba_8 *p, uint16_t w, uint16_t h, unsigned int f) {
1162 uint32_t i;
1163
1164 struct pixel_rgb_8 a;
1165
1166 /* Get rid of alpha channel */
1167 for(i = 0; i < (uint32_t) w * h; i++) {
1168 a.r = p[i].pixel.r;
1169 a.g = p[i].pixel.g;
1170 a.b = p[i].pixel.b;
1171
1172 if(files_write(f, (void *) &a, sizeof(a)) != 0) return(-1);
1173 }
1174
1175 return(0);
1176 }
1177 #endif
1178
1179 /**
1180 * @brief Write Sun RAS image using 32 bits (RGBA) per pixel.
1181 *
1182 * @param[in] p Image pixel data
1183 * @param[in] w Image width in pixels
1184 * @param[in] h Image height in pixels
1185 * @param[in] s File name to write
1186 *
1187 * @retval Zero on success
1188 * @retval Non-zero on failure
1189 *
1190 */
1191
1192 #if ! defined(PROG_DISABLE_IMAGE)
image_ras_write_32(struct pixel_rgba_8 * p,unsigned int w,unsigned int h,char * s)1193 int image_ras_write_32(struct pixel_rgba_8 *p, unsigned int w, unsigned int h, char *s) {
1194 unsigned int f;
1195
1196 if((f = files_open(s, FILE_FLAG_NOCOMPRESS, FILE_MODE_WRITE_AND_TRUNCATE)) == 0) return(-1);
1197
1198 if(image_ras_header(IMAGE_RAS_STANDARD, (uint16_t) w, (uint16_t) h, 32, f) != 0) return(-1);
1199 if(image_ras_channs(p, w, h, f) != 0) return(-1);
1200
1201 (void) files_close(f);
1202
1203 return(0);
1204 }
1205
image_ras_header(int8_t t,uint16_t w,uint16_t h,uint16_t b,unsigned int f)1206 static int image_ras_header(int8_t t, uint16_t w, uint16_t h, uint16_t b, unsigned int f) {
1207 struct ras_header d;
1208
1209 (void) memset((void *) &d, 0, sizeof(d));
1210
1211 d.ras_magic = endian_be_int32(IMAGE_RAS_TAG);
1212
1213 /* Dimensions */
1214 d.ras_width = endian_be_int32((int32_t) w);
1215 d.ras_height = endian_be_int32((int32_t) h);
1216 d.ras_depth = endian_be_int32((int32_t) b);
1217 d.ras_length = endian_be_int32(d.ras_width * d.ras_height * d.ras_depth);
1218
1219 /* Storage type */
1220 d.ras_type = endian_be_int32((int32_t) t);
1221
1222 /* Colormap */
1223 d.ras_maptype = endian_be_int32(0);
1224 d.ras_maplength = endian_be_int32(0);
1225
1226 return(files_write(f, (void *) &d, sizeof(d)));
1227 }
1228
image_ras_channs(struct pixel_rgba_8 * p,uint16_t w,uint16_t h,unsigned int f)1229 static int image_ras_channs(struct pixel_rgba_8 *p, uint16_t w, uint16_t h, unsigned int f) {
1230 #if defined(IS_BIGENDIAN)
1231 uint32_t i;
1232
1233 struct pixel_rgba_8 r;
1234
1235 for(i = 0; i < (uint32_t) (w * h); i++) {
1236 r.pixel.p = endian_le_uint32(p[i].pixel.p);
1237
1238 if(files_write(f, (void *) &r.pixel.p, sizeof(r.pixel.p)) != 0) return(-1);
1239 }
1240
1241 return(0);
1242 #else
1243 return(files_write(f, (void *) p, (size_t) w * h * sizeof(struct pixel_rgba_8)));
1244 #endif
1245 }
1246 #endif
1247
1248 /**
1249 * @brief Write Silicon Graphics RGB image using 32 bits (RGBA) per pixel.
1250 *
1251 * @param[in] p Image pixel data
1252 * @param[in] w Image width in pixels
1253 * @param[in] h Image height in pixels
1254 * @param[in] s File name to write
1255 *
1256 * @retval Zero on success
1257 * @retval Non-zero on failure
1258 *
1259 */
1260
1261 #if ! defined(PROG_DISABLE_IMAGE)
image_rgb_write_32(struct pixel_rgba_8 * p,unsigned int w,unsigned int h,char * s)1262 int image_rgb_write_32(struct pixel_rgba_8 *p, unsigned int w, unsigned int h, char *s) {
1263 unsigned int f;
1264
1265 if((f = files_open(s, FILE_FLAG_NOCOMPRESS, FILE_MODE_WRITE_AND_TRUNCATE)) == 0) return(-1);
1266
1267 if(image_rgb_header(IMAGE_RGB_STORAGE_VERBATIM, (uint16_t) w, (uint16_t) h, 4, 1, f) != 0) return(-1);
1268 if(image_rgb_channs(p, w, h, f) != 0) return(-1);
1269
1270 (void) files_close(f);
1271
1272 return(0);
1273 }
1274
image_rgb_header(int8_t t,uint16_t w,uint16_t h,uint16_t b,int8_t c,unsigned int f)1275 static int image_rgb_header(int8_t t, uint16_t w, uint16_t h, uint16_t b, int8_t c, unsigned int f) {
1276 char *s, *k;
1277
1278 struct rgb_header d;
1279
1280 (void) memset((void *) &d, 0, sizeof(d));
1281
1282 d.magic = endian_be_int16(IMAGE_RGB_TAG);
1283
1284 /* Storage type */
1285 d.storage = endian_be_int8(t);
1286 d.bpc = endian_be_int8(c);
1287
1288 /* Dimensions */
1289 d.dimension = endian_be_uint16(3);
1290 d.xsize = endian_be_uint16(w);
1291 d.ysize = endian_be_uint16(h);
1292 d.zsize = endian_be_uint16(b);
1293
1294 /* Pixel limits */
1295 d.pixmin = endian_be_int32(0);
1296 d.pixmax = endian_be_int32(255);
1297
1298 /* Image name */
1299 if((s = files_get_name(f)) != NULL) {
1300 if((k = strrchr((const char *) s, CONFIG_PATH_DELIM)) == NULL) k = s;
1301 else k += sizeof(char);
1302 }
1303 else k = "";
1304
1305 (void) snprintf((char *) d.imagename, sizeof(d.imagename), "%s", k);
1306
1307 /* Colormap */
1308 d.colormap = endian_be_int32(0);
1309
1310 return(files_write(f, (void *) &d, sizeof(d)));
1311 }
1312
image_rgb_channs(struct pixel_rgba_8 * p,uint16_t w,uint16_t h,unsigned int f)1313 static int image_rgb_channs(struct pixel_rgba_8 *p, uint16_t w, uint16_t h, unsigned int f) {
1314 uint8_t r, g, b, a;
1315 uint32_t i, j, k;
1316
1317 /* Red channel */
1318 for(i = (uint32_t) h; i != 0; i--) {
1319 k = (i - 1) * w;
1320
1321 for(j = 0; j < (uint32_t) w; j++) {
1322 r = p[k + j].pixel.r;
1323
1324 if(files_write(f, (void *) &r, sizeof(r)) != 0) return(-1);
1325 }
1326 }
1327
1328 /* Green channel */
1329 for(i = (uint32_t) h; i != 0; i--) {
1330 k = (i - 1) * w;
1331
1332 for(j = 0; j < (uint32_t) w; j++) {
1333 g = p[k + j].pixel.g;
1334
1335 if(files_write(f, (void *) &g, sizeof(g)) != 0) return(-1);
1336 }
1337 }
1338
1339 /* Blue channel */
1340 for(i = (uint32_t) h; i != 0; i--) {
1341 k = (i - 1) * w;
1342
1343 for(j = 0; j < (uint32_t) w; j++) {
1344 b = p[k + j].pixel.b;
1345
1346 if(files_write(f, (void *) &b, sizeof(b)) != 0) return(-1);
1347 }
1348 }
1349
1350 /* Alpha channel */
1351 for(i = (uint32_t) h; i != 0; i--) {
1352 k = (i - 1) * w;
1353
1354 for(j = 0; j < (uint32_t) w; j++) {
1355 a = p[k + j].pixel.a;
1356
1357 if(files_write(f, (void *) &a, sizeof(a)) != 0) return(-1);
1358 }
1359 }
1360
1361 return(0);
1362 }
1363 #endif
1364
1365 /**
1366 * @brief Write Autodesk RLA image using 32 bits (RGBA) per pixel.
1367 *
1368 * @param[in] p Image pixel data
1369 * @param[in] w Image width in pixels
1370 * @param[in] h Image height in pixels
1371 * @param[in] s File name to write
1372 *
1373 * @retval Zero on success
1374 * @retval Non-zero on failure
1375 *
1376 */
1377
1378 #if ! defined(PROG_DISABLE_IMAGE)
image_rla_write_32(struct pixel_rgba_8 * p,unsigned int w,unsigned int h,char * s)1379 int image_rla_write_32(struct pixel_rgba_8 *p, unsigned int w, unsigned int h, char *s) {
1380 unsigned int f;
1381
1382 if((f = files_open(s, FILE_FLAG_NOCOMPRESS, FILE_MODE_WRITE_AND_TRUNCATE)) == 0) return(-1);
1383
1384 if(image_rla_header((uint16_t) w, (uint16_t) h, f) != 0) return(-1);
1385 if(image_rla_offset((uint16_t) h, f) != 0) return(-1);
1386 if(image_rla_channs(p, w, h, f) != 0) return(-1);
1387
1388 (void) files_close(f);
1389
1390 return(0);
1391 }
1392
image_rla_header(uint16_t w,uint16_t h,unsigned int f)1393 static int image_rla_header(uint16_t w, uint16_t h, unsigned int f) {
1394 unsigned int i;
1395
1396 char *s, *k;
1397
1398 time_t m;
1399
1400 struct tm *t;
1401 struct rla_header d;
1402
1403 (void) memset((void *) &d, 0, sizeof(d));
1404
1405 /* Revision */
1406 d.revision = endian_be_uint16(0xfffe);
1407
1408 /* Dimensions */
1409 d.window.left = endian_be_uint16(0);
1410 d.window.right = endian_be_uint16(w - 1);
1411 d.window.bottom = endian_be_uint16(0);
1412 d.window.top = endian_be_uint16(h - 1);
1413
1414 d.active_window.left = d.window.left;
1415 d.active_window.right = d.window.right;
1416 d.active_window.bottom = d.window.bottom;
1417 d.active_window.top = d.window.top;
1418
1419 /* Specifications */
1420 d.frame = endian_be_uint16(1);
1421 d.field = endian_be_uint16(0);
1422 d.job_num = endian_be_uint32(0);
1423
1424 d.num_chan = endian_be_uint16(3);
1425 d.chan_bits = endian_be_uint16(8);
1426 d.storage_type = endian_be_uint16(0);
1427
1428 d.num_matte = endian_be_uint16(1);
1429 d.matte_bits = endian_be_uint16(8);
1430 d.matte_type = endian_be_uint16(0);
1431
1432 d.num_aux = endian_be_uint16(0);
1433 d.aux_bits = endian_be_uint16(0);
1434 d.aux_type = endian_be_uint16(0);
1435
1436 (void) snprintf((char *) d.chan, sizeof(d.chan), "rgb");
1437 (void) snprintf((char *) d.gamma, sizeof(d.gamma), "%.4f", 2.2);
1438
1439 /* Aspect ratio */
1440 for(i = 0; ; i++) {
1441 if(rla_header_aspect_t[i].s == NULL) {
1442 (void) snprintf((char *) d.aspect, sizeof(d.aspect), "%s_%u_%u", APPLICATION_EXEC, (unsigned int) w, (unsigned int) h);
1443
1444 break;
1445 }
1446
1447 if(rla_header_aspect_t[i].w == w && rla_header_aspect_t[i].h == h) {
1448 (void) snprintf((char *) d.aspect, sizeof(d.aspect), "%s", rla_header_aspect_t[i].s);
1449
1450 break;
1451 }
1452 }
1453
1454 (void) snprintf((char *) d.aspect_ratio, sizeof(d.aspect_ratio), "%.4f", (float) w / h);
1455
1456 /* Chromaticity */
1457 (void) snprintf((char *) d.red_pri, sizeof(d.red_pri), "%.4f %.4f", 0.6400, 0.3300);
1458 (void) snprintf((char *) d.green_pri, sizeof(d.green_pri), "%.4f %.4f", 0.3000, 0.6000);
1459 (void) snprintf((char *) d.blue_pri, sizeof(d.blue_pri), "%.4f %.4f", 0.1500, 0.0600);
1460 (void) snprintf((char *) d.white_pt, sizeof(d.white_pt), "%.4f %.4f", 0.3127, 0.3290);
1461
1462 /* Creator info */
1463 if((s = files_get_name(f)) != NULL) {
1464 if((k = strrchr((const char *) s, CONFIG_PATH_DELIM)) == NULL) k = s;
1465 else k += sizeof(char);
1466 }
1467 else k = "";
1468
1469 (void) snprintf((char *) d.name, sizeof(d.name), "%s", k);
1470 (void) snprintf((char *) d.program, sizeof(d.program), APPLICATION_NAME " " APPLICATION_VERSION " (" CONFIG_MACH ")");
1471 (void) snprintf((char *) d.machine, sizeof(d.machine), "%s", node_get_node());
1472 (void) snprintf((char *) d.user, sizeof(d.user), "%s", node_get_user());
1473
1474 /* Creation date and time */
1475 if((m = time(NULL)) != (time_t) -1) {
1476 if((t = localtime(&m)) != NULL) (void) strftime((char *) d.date, sizeof(d.date), "%b %d %H:%M %Y", t);
1477 }
1478
1479 /* Offset to next image in file */
1480 d.next = endian_be_uint32(0);
1481
1482 return(files_write(f, (void *) &d, sizeof(d)));
1483 }
1484
image_rla_offset(uint16_t h,unsigned int f)1485 static int image_rla_offset(uint16_t h, unsigned int f) {
1486 uint32_t i, a;
1487
1488 a = endian_be_uint32(0);
1489
1490 for(i = 0; i < (uint32_t) h; i++) {
1491 if(files_write(f, (void *) &a, sizeof(a)) != 0) return(-1);
1492 }
1493
1494 return(0);
1495 }
1496
image_rla_channs(struct pixel_rgba_8 * p,uint16_t w,uint16_t h,unsigned int f)1497 static int image_rla_channs(struct pixel_rgba_8 *p, uint16_t w, uint16_t h, unsigned int f) {
1498 long u, v;
1499
1500 int8_t c;
1501 uint8_t a, g;
1502 uint16_t d, b;
1503 uint32_t i, j, t, k, n, e;
1504
1505 n = (uint32_t) sizeof(struct rla_header) + (sizeof(uint32_t) * h);
1506
1507 for(i = 0; i < (uint32_t) h; i++) {
1508 /* Add this scanline to offset table */
1509 e = endian_be_uint32(n);
1510
1511 if((u = files_tell(f)) == -1) return(-1);
1512
1513 if(files_seek(f, (long) sizeof(struct rla_header) + (sizeof(uint32_t) * i)) != 0) return(-1);
1514 if(files_write(f, (void *) &e, sizeof(e)) != 0) return(-1);
1515 if(files_seek(f, u) != 0) return(-1);
1516
1517 k = (uint32_t) i * w;
1518
1519 /* RLA style RLE compression */
1520 for(t = 0; t < 4; t++) {
1521 d = 0;
1522
1523 if((v = files_tell(f)) == -1) return(-1);
1524 if(files_write(f, (void *) &d, sizeof(d)) != 0) return(-1);
1525
1526 c = 0;
1527
1528 switch(t) {
1529 case 0:
1530 a = p[k].pixel.r;
1531
1532 break;
1533 case 1:
1534 a = p[k].pixel.g;
1535
1536 break;
1537 case 2:
1538 a = p[k].pixel.b;
1539
1540 break;
1541 default:
1542 a = p[k].pixel.a;
1543
1544 break;
1545 }
1546
1547 for(j = 0; j < (uint32_t) w; j++) {
1548 switch(t) {
1549 case 0:
1550 g = p[k + j].pixel.r;
1551
1552 break;
1553 case 1:
1554 g = p[k + j].pixel.g;
1555
1556 break;
1557 case 2:
1558 g = p[k + j].pixel.b;
1559
1560 break;
1561 default:
1562 g = p[k + j].pixel.a;
1563
1564 break;
1565 }
1566
1567 if(c < 126 && (a == g)) ++c;
1568 else {
1569 d += (uint16_t) image_rla_channs_op(c, a, f);
1570
1571 c = 1;
1572 a = g;
1573 }
1574 }
1575
1576 if(c > 0) d += (uint16_t) image_rla_channs_op(c, a, f);
1577
1578 n += (uint32_t) d;
1579 n += (uint32_t) sizeof(d);
1580
1581 b = endian_be_uint16(d);
1582
1583 if((u = files_tell(f)) == -1) return(-1);
1584
1585 if(files_seek(f, v) != 0) return(-1);
1586 if(files_write(f, (void *) &b, sizeof(b)) != 0) return(-1);
1587 if(files_seek(f, u) != 0) return(-1);
1588 }
1589 }
1590
1591 return(0);
1592 }
1593
image_rla_channs_op(int8_t c,uint8_t a,unsigned int f)1594 static size_t image_rla_channs_op(int8_t c, uint8_t a, unsigned int f) {
1595 unsigned int i;
1596
1597 int8_t n;
1598
1599 size_t r;
1600
1601 r = 0;
1602
1603 if(c < 3) {
1604 n = -(c);
1605
1606 if(files_write(f, (void *) &n, sizeof(n)) != 0) return(r);
1607
1608 r += sizeof(n);
1609
1610 for(i = 0; i < (unsigned int) c; i++) {
1611 if(files_write(f, (void *) &a, sizeof(a)) != 0) return(r);
1612
1613 r += sizeof(a);
1614 }
1615 }
1616 else {
1617 n = c - 1;
1618
1619 if(files_write(f, (void *) &n, sizeof(n)) != 0) return(r);
1620
1621 r += sizeof(n);
1622
1623 if(files_write(f, (void *) &a, sizeof(a)) != 0) return(r);
1624
1625 r += sizeof(a);
1626 }
1627
1628 return(r);
1629 }
1630 #endif
1631
1632 /**
1633 * @brief Write Truevision TGA image using 32 bits (RGBA) per pixel.
1634 *
1635 * @param[in] p Image pixel data
1636 * @param[in] w Image width in pixels
1637 * @param[in] h Image height in pixels
1638 * @param[in] s File name to write
1639 *
1640 * @retval Zero on success
1641 * @retval Non-zero on failure
1642 *
1643 */
1644
1645 #if ! defined(PROG_DISABLE_IMAGE)
image_tga_write_32(struct pixel_rgba_8 * p,unsigned int w,unsigned int h,char * s)1646 int image_tga_write_32(struct pixel_rgba_8 *p, unsigned int w, unsigned int h, char *s) {
1647 unsigned int f;
1648
1649 uint32_t a;
1650
1651 time_t v;
1652
1653 v = time(NULL);
1654 a = (uint32_t) sizeof(struct tga_header) + ((w * h) * sizeof(struct pixel_rgba_8));
1655
1656 if((f = files_open(s, FILE_FLAG_NOCOMPRESS, FILE_MODE_WRITE_AND_TRUNCATE)) == 0) return(-1);
1657
1658 if(image_tga_write_header(IMAGE_TGA_STORAGE_VERBATIM, (uint16_t) w, (uint16_t) h, 32, f) != 0) return(-1);
1659 if(image_tga_write_channs(p, w, h, f) != 0) return(-1);
1660 if(image_tga_write_extens(w, h, v, time(NULL), f) != 0) return(-1);
1661 if(image_tga_write_footer(a, f) != 0) return(-1);
1662
1663 (void) files_close(f);
1664
1665 return(0);
1666 }
1667
image_tga_write_header(uint8_t t,uint16_t w,uint16_t h,uint8_t b,unsigned int f)1668 static int image_tga_write_header(uint8_t t, uint16_t w, uint16_t h, uint8_t b, unsigned int f) {
1669 struct tga_header d;
1670
1671 d.d = endian_le_uint8(0);
1672 d.m = endian_le_uint8(0);
1673 d.t = endian_le_uint8(t);
1674
1675 d.c.o = endian_le_uint16(0);
1676 d.c.n = endian_le_uint16(0);
1677 d.c.b = endian_le_uint8(0);
1678
1679 /* Dimensions */
1680 d.i.x = endian_le_int16(0);
1681 d.i.y = endian_le_int16(0);
1682 d.i.w = endian_le_uint16(w);
1683 d.i.h = endian_le_uint16(h);
1684 d.i.b = endian_le_uint8(b);
1685
1686 /* Attribute bits */
1687 d.i.a = endian_le_uint8(0);
1688
1689 (void) image_tga_write_header_aux(3, &d.i.a);
1690 (void) image_tga_write_header_ltr(0, &d.i.a);
1691 (void) image_tga_write_header_ttb(0, &d.i.a);
1692
1693 return(files_write(f, (void *) &d, sizeof(d)));
1694 }
1695
image_tga_write_header_aux(uint8_t a,uint8_t * d)1696 static void image_tga_write_header_aux(uint8_t a, uint8_t *d) {
1697 *d &= 0xf0;
1698 *d |= (1 << a);
1699 }
1700
image_tga_write_header_ltr(uint8_t a,uint8_t * d)1701 static void image_tga_write_header_ltr(uint8_t a, uint8_t *d) {
1702 *d &= 0xef;
1703 *d |= (a << 4);
1704 }
1705
image_tga_write_header_ttb(uint8_t a,uint8_t * d)1706 static void image_tga_write_header_ttb(uint8_t a, uint8_t *d) {
1707 *d &= 0xdf;
1708 *d |= (a << 5);
1709 }
1710
image_tga_write_channs(struct pixel_rgba_8 * p,uint16_t w,uint16_t h,unsigned int f)1711 static int image_tga_write_channs(struct pixel_rgba_8 *p, uint16_t w, uint16_t h, unsigned int f) {
1712 uint32_t i, j, k, l;
1713
1714 struct pixel_bgra_8 a;
1715
1716 /* Swap alpha byte position and turn image upside down */
1717 for(i = (uint32_t) h; i != 0; i--) {
1718 k = (i - 1) * w;
1719
1720 for(j = 0; j < (uint32_t) w; j++) {
1721 l = k + j;
1722 #if defined(IS_BIGENDIAN)
1723 a.pixel.b = p[l].pixel.b;
1724 a.pixel.g = p[l].pixel.g;
1725 a.pixel.r = p[l].pixel.r;
1726 a.pixel.a = p[l].pixel.a;
1727 #else
1728 a.pixel.b = p[l].pixel.a;
1729 a.pixel.g = p[l].pixel.r;
1730 a.pixel.r = p[l].pixel.g;
1731 a.pixel.a = p[l].pixel.b;
1732 #endif
1733 if(files_write(f, (void *) &a.pixel.p, sizeof(a.pixel.p)) != 0) return(-1);
1734 }
1735 }
1736
1737 return(0);
1738 }
1739
image_tga_write_footer(uint32_t a,unsigned int f)1740 static int image_tga_write_footer(uint32_t a, unsigned int f) {
1741 struct tga_footer d;
1742
1743 (void) memset((void *) &d, 0, sizeof(a));
1744
1745 d.a = endian_le_uint32(a);
1746 d.b = endian_le_uint32(0);
1747
1748 (void) snprintf((char *) d.s, sizeof(d.s), IMAGE_TGA_FOOTER_SIGNATURE);
1749
1750 return(files_write(f, (void *) &d, sizeof(d)));
1751 }
1752
image_tga_write_extens(uint16_t w,uint16_t h,time_t v,time_t u,unsigned int f)1753 static int image_tga_write_extens(uint16_t w, uint16_t h, time_t v, time_t u, unsigned int f) {
1754 char *s, *k;
1755
1756 time_t m;
1757
1758 struct tm *t;
1759 struct tga_extens d;
1760
1761 (void) memset((void *) &d, 0, sizeof(d));
1762
1763 d.t = endian_le_uint16((uint16_t) sizeof(struct tga_extens));
1764
1765 /* Author name */
1766 (void) snprintf((char *) d.n, sizeof(d.n), "%s", node_get_user());
1767
1768 /* Creation date */
1769 d.e.m = endian_le_uint16(0);
1770 d.e.d = endian_le_uint16(0);
1771 d.e.y = endian_le_uint16(0);
1772 d.e.h = endian_le_uint16(0);
1773 d.e.i = endian_le_uint16(0);
1774 d.e.s = endian_le_uint16(0);
1775
1776 if((m = time(NULL)) != (time_t) -1) {
1777 if((t = localtime(&m)) != NULL) {
1778 d.e.m = endian_le_uint16((uint16_t) t->tm_mon + 1);
1779 d.e.d = endian_le_uint16((uint16_t) t->tm_mday);
1780 d.e.y = endian_le_uint16((uint16_t) t->tm_year + 1900);
1781 d.e.h = endian_le_uint16((uint16_t) t->tm_hour);
1782 d.e.i = endian_le_uint16((uint16_t) t->tm_min);
1783 d.e.s = endian_le_uint16((uint16_t) t->tm_sec);
1784 }
1785 }
1786
1787 /* Job name and time */
1788 if((s = files_get_name(f)) != NULL) {
1789 if((k = strrchr((const char *) s, CONFIG_PATH_DELIM)) == NULL) k = s;
1790 else k += sizeof(char);
1791 }
1792 else k = "";
1793
1794 (void) snprintf((char *) d.d, sizeof(d.d), "%s", k);
1795
1796 (void) image_tga_write_extens_time(&d, v, u);
1797
1798 /* Software id and version */
1799 (void) snprintf((char *) d.s, sizeof(d.s), APPLICATION_NAME " " APPLICATION_VERSION);
1800
1801 d.v.v = endian_le_uint16(0);
1802 d.v.w = ' ';
1803
1804 /* Key color */
1805 d.k = endian_le_uint32(0);
1806
1807 /* Pixel aspect ratio and gamma value */
1808 (void) image_tga_write_extens_aspect(&d, w, h);
1809
1810 /* Offsets */
1811 d.x = endian_le_uint32(0);
1812 d.y = endian_le_uint32(0);
1813 d.l = endian_le_uint32(0);
1814
1815 /* Attributes type */
1816 d.a = endian_le_uint8(3);
1817
1818 return(files_write(f, (void *) &d, sizeof(d)));
1819 }
1820
image_tga_write_extens_aspect(struct tga_extens * d,uint16_t w,uint16_t h)1821 static void image_tga_write_extens_aspect(struct tga_extens *d, uint16_t w, uint16_t h) {
1822 double r;
1823
1824 r = maths_hcf((double) w, (double) h);
1825
1826 d->p.n = endian_le_uint16((uint16_t) round((double) w / r));
1827 d->p.d = endian_le_uint16((uint16_t) round((double) h / r));
1828
1829 d->g.n = endian_le_uint16(2);
1830 d->g.d = endian_le_uint16(2);
1831 }
1832
image_tga_write_extens_time(struct tga_extens * d,time_t v,time_t u)1833 static void image_tga_write_extens_time(struct tga_extens *d, time_t v, time_t u) {
1834 time_t t, h;
1835
1836 if(v == (time_t) -1 || u == (time_t) -1) {
1837 d->f.h = endian_le_uint16(0);
1838 d->f.i = endian_le_uint16(0);
1839 d->f.s = endian_le_uint16(0);
1840
1841 return;
1842 }
1843
1844 t = u - v;
1845 h = t / (60 * 60);
1846
1847 if(h > 65535) h = 65535;
1848
1849 d->f.h = endian_le_uint16((uint16_t) h);
1850 d->f.i = endian_le_uint16((uint16_t) (t % (60 * 60)) / 60);
1851 d->f.s = endian_le_uint16((uint16_t) t % 60);
1852 }
1853 #endif
1854
1855 /**
1856 * @brief Write image by format.
1857 *
1858 * @param[in] p Image pixel data
1859 * @param[in] w Image width in pixels
1860 * @param[in] h Image height in pixels
1861 * @param[in] s File name to write
1862 * @param[in] c File format to write (IMAGE_FORMAT_*)
1863 *
1864 * @retval Zero on success
1865 * @retval Non-zero on failure
1866 *
1867 */
1868
1869 #if ! defined(PROG_DISABLE_IMAGE)
image_write_32(struct pixel_rgba_8 * p,unsigned int w,unsigned int h,char * s,int c)1870 int image_write_32(struct pixel_rgba_8 *p, unsigned int w, unsigned int h, char *s, int c) {
1871 switch(c) {
1872 case IMAGE_FORMAT_BMP:
1873 return(image_bmp_write_32(p, w, h, s));
1874 case IMAGE_FORMAT_CEL:
1875 return(image_cel_write_32(p, w, h, s));
1876 case IMAGE_FORMAT_PIX:
1877 return(image_pix_write_32(p, w, h, s));
1878 case IMAGE_FORMAT_PPM:
1879 return(image_ppm_write_32(p, w, h, s));
1880 case IMAGE_FORMAT_RAS:
1881 return(image_ras_write_32(p, w, h, s));
1882 case IMAGE_FORMAT_RGB:
1883 return(image_rgb_write_32(p, w, h, s));
1884 case IMAGE_FORMAT_RLA:
1885 return(image_rla_write_32(p, w, h, s));
1886 case IMAGE_FORMAT_TGA:
1887 return(image_tga_write_32(p, w, h, s));
1888 default:
1889 (void) flush_error();
1890
1891 LOGWARN(
1892 ERROR_SLIGHT, SUBSYSTEM,
1893 _("Image type %d is not supported"),
1894 c
1895 );
1896
1897 break;
1898 }
1899
1900 return(-1);
1901 }
1902 #endif
1903
1904 /**
1905 * @brief Get image file name suffix by IMAGE_FORMAT_* tag.
1906 *
1907 * @param[in] c File format (IMAGE_FORMAT_*)
1908 *
1909 * @retval Image file name suffix
1910 *
1911 */
1912
1913 #if ! defined(PROG_DISABLE_IMAGE)
image_get_suffix_by_type(int c)1914 const char *image_get_suffix_by_type(int c) {
1915 return(img_suffix[c]);
1916 }
1917 #endif
1918
image_get_type_by_suffix(char * s)1919 static int image_get_type_by_suffix(char *s) {
1920 int i;
1921
1922 size_t t;
1923
1924 if(s == NULL) return(IMAGE_FORMAT_NONE);
1925
1926 t = str_len(s, STRING_ASCII);
1927
1928 for(i = 1; ; i++) {
1929 if(img_suffix[i] == NULL) break;
1930 if(str_len(img_suffix[i], STRING_ASCII) != t) continue;
1931 if(strncasecmp((const char *) img_suffix[i], (const char *) s, t) == 0) return(i);
1932 }
1933
1934 return(IMAGE_FORMAT_NONE);
1935 }
1936