1 /*
2 * Dump an image block as a png image.
3 * This routine is used by several terminal drivers so it gets a file by itself.
4 * May 2016 Daniel Sebald: Support routines to write image in Base64 encoding.
5 */
6 #ifdef TERM_BODY
7 #ifndef WRITE_PNG_IMAGE
8 #define WRITE_PNG_IMAGE
9
10 typedef struct base64state {
11 int shift;
12 unsigned char bit6;
13 unsigned int byte4;
14 FILE* out;
15 } base64s;
16
17 static const unsigned char base64_lut[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
18
19 void
init_base64_state_data(base64s * b64,FILE * out)20 init_base64_state_data (base64s *b64, FILE *out) {
21 b64->shift = 6;
22 b64->bit6 = 0;
23 b64->byte4 = 0;
24 b64->out = out;
25 }
26
27 static int
piecemeal_write_base64_data_finish(base64s * b64)28 piecemeal_write_base64_data_finish (base64s *b64) {
29
30 if (b64->shift < 6) {
31 if (fputc(base64_lut[b64->bit6 & 0x3F], b64->out) == EOF)
32 return 1;
33 if (b64->byte4 == 0)
34 b64->byte4 = 3;
35 else
36 b64->byte4--;
37 }
38 while (b64->byte4 > 0) {
39 if (fputc('=', b64->out) == EOF)
40 return 1;
41 b64->byte4--;
42 }
43
44 return 0;
45 }
46
47 /* To use repeatedly, must initialize base64state first, then when done call finish. See write_base64_data(). */
48 static int
piecemeal_write_base64_data(const unsigned char * data,unsigned int length,base64s * b64)49 piecemeal_write_base64_data (const unsigned char *data, unsigned int length, base64s *b64) {
50 unsigned int i_data = 0;
51
52 while (1) {
53 unsigned int databyte = 0;
54 if (b64->shift > 0) {
55 if (i_data >= length)
56 break;
57 else {
58 databyte = data[i_data++];
59 b64->shift -= 8;
60 if (b64->shift >= 0)
61 b64->bit6 |= (databyte << b64->shift);
62 else
63 b64->bit6 |= (databyte >> -b64->shift);
64 }
65 }
66 if (fputc(base64_lut[b64->bit6 & 0x3F], b64->out) == EOF)
67 return 1;
68 b64->shift += 6;
69 b64->bit6 = (databyte << b64->shift);
70 if (b64->byte4 == 0)
71 b64->byte4 = 3;
72 else
73 b64->byte4--;
74 }
75
76 return 0;
77 }
78
79 #ifdef HAVE_CAIROPDF
80
81 #include "cairo-pdf.h"
82 #include "wxterminal/gp_cairo.h"
83 #include "wxterminal/gp_cairo_helpers.h"
84
85 /* cairo PNG code */
86 static int
write_png_image(unsigned m,unsigned n,coordval * image,t_imagecolor color_mode,const char * filename)87 write_png_image (unsigned m, unsigned n, coordval *image, t_imagecolor color_mode, const char *filename) {
88 cairo_surface_t *image_surface;
89 cairo_status_t cairo_stat;
90 unsigned int *image255;
91
92 image255 = gp_cairo_helper_coordval_to_chars(image, m, n, color_mode);
93 image_surface = cairo_image_surface_create_for_data((unsigned char*) image255, CAIRO_FORMAT_ARGB32, m, n, 4*m);
94 cairo_stat = cairo_surface_write_to_png(image_surface, filename);
95 cairo_surface_destroy(image_surface);
96 if (cairo_stat != CAIRO_STATUS_SUCCESS) {
97 int_warn(NO_CARET, "write_png_image cairo: could not write image file '%s': %s.", filename, cairo_status_to_string(cairo_stat));
98 return 1;
99 } else
100 return 0;
101 }
102
103 cairo_status_t
cairo_write_base64_callback(void * closure,const unsigned char * data,unsigned int length)104 cairo_write_base64_callback (void *closure, const unsigned char *data, unsigned int length) {
105 if (piecemeal_write_base64_data (data, length, closure) == 0)
106 return CAIRO_STATUS_SUCCESS;
107 else
108 return CAIRO_STATUS_WRITE_ERROR;
109 }
110
111 static int
write_png_base64_image(unsigned m,unsigned n,coordval * image,t_imagecolor color_mode,FILE * out)112 write_png_base64_image (unsigned m, unsigned n, coordval *image, t_imagecolor color_mode, FILE *out) {
113 cairo_surface_t *image_surface;
114 cairo_status_t cairo_stat;
115 unsigned int *image255;
116 base64s *b64;
117 int retval = 0;
118
119 b64 = gp_alloc(sizeof(base64s), "base64s");
120 if (b64 == NULL)
121 return 1;
122
123 image255 = gp_cairo_helper_coordval_to_chars(image, m, n, color_mode);
124 image_surface = cairo_image_surface_create_for_data((unsigned char*) image255, CAIRO_FORMAT_ARGB32, m, n, 4*m);
125
126 init_base64_state_data (b64, out);
127 cairo_stat = cairo_surface_write_to_png_stream(image_surface, cairo_write_base64_callback, b64);
128 cairo_surface_destroy(image_surface);
129 if (cairo_stat != CAIRO_STATUS_SUCCESS) {
130 int_warn(NO_CARET, "write_png_image cairo: could not write image file: %s.", cairo_status_to_string(cairo_stat));
131 retval = 1;
132 } else
133 retval = piecemeal_write_base64_data_finish (b64);
134
135 free(b64);
136
137 return retval;
138 }
139
140 #else /* libgd PNG code mainly taken from gd.trm */
141 #include <gd.h>
142
143 gdImagePtr
construct_gd_image(unsigned M,unsigned N,coordval * image,t_imagecolor color_mode)144 construct_gd_image (unsigned M, unsigned N, coordval *image, t_imagecolor color_mode) {
145 int m, n, pixel;
146 gdImagePtr im;
147
148 im = gdImageCreateTrueColor(M, N);
149 if (!im) {
150 int_warn(NO_CARET, "libgd: failed to create image structure");
151 return im;
152 }
153 /* gdImageColorAllocateAlpha(im, 255, 255, 255, 127); */
154 gdImageSaveAlpha(im, 1);
155 gdImageAlphaBlending(im, 0);
156
157 if (color_mode == IC_RGBA) {
158 /* RGB + Alpha channel */
159 for (n=0; n<N; n++) {
160 for (m=0; m<M; m++) {
161 rgb_color rgb1;
162 rgb255_color rgb255;
163 int alpha;
164 rgb1.r = *image++;
165 rgb1.g = *image++;
166 rgb1.b = *image++;
167 alpha = *image++;
168 alpha = 127 - (alpha>>1); /* input is [0:255] but gd wants [127:0] */
169 rgb255_from_rgb1( rgb1, &rgb255 );
170 pixel = gdImageColorResolveAlpha(im, (int)rgb255.r, (int)rgb255.g, (int)rgb255.b, alpha);
171 gdImageSetPixel( im, m, n, pixel );
172 }
173 }
174 } else if (color_mode == IC_RGB) {
175 /* TrueColor 24-bit color mode */
176 for (n=0; n<N; n++) {
177 for (m=0; m<M; m++) {
178 rgb_color rgb1;
179 rgb255_color rgb255;
180 rgb1.r = *image++;
181 rgb1.g = *image++;
182 rgb1.b = *image++;
183 rgb255_from_rgb1( rgb1, &rgb255 );
184 pixel = gdImageColorResolve(im, (int)rgb255.r, (int)rgb255.g, (int)rgb255.b );
185 gdImageSetPixel( im, m, n, pixel );
186 }
187 }
188 } else if (color_mode == IC_PALETTE) {
189 /* Palette color lookup from gray value */
190 for (n=0; n<N; n++) {
191 for (m=0; m<M; m++) {
192 rgb255_color rgb;
193 if (isnan(*image)) {
194 /* FIXME: tried to take the comment from gd.trm into account but needs a testcase */
195 pixel = gdImageColorResolveAlpha(im, 0, 0, 0, 127);
196 image++;
197 } else {
198 rgb255maxcolors_from_gray( *image++, &rgb );
199 pixel = gdImageColorResolve( im, (int)rgb.r, (int)rgb.g, (int)rgb.b );
200 }
201 gdImageSetPixel( im, m, n, pixel );
202 }
203 }
204 }
205
206 return im;
207 }
208
209 static int
write_png_image(unsigned M,unsigned N,coordval * image,t_imagecolor color_mode,const char * filename)210 write_png_image (unsigned M, unsigned N, coordval *image, t_imagecolor color_mode, const char *filename) {
211 gdImagePtr im;
212 FILE *out;
213
214 im = construct_gd_image (M, N, image, color_mode);
215 if (!im)
216 return 1;
217
218 out = fopen(filename, "wb");
219 if (!out) {
220 int_warn(NO_CARET, "write_png_image libgd: could not write image file '%s'", filename);
221 gdImageDestroy(im);
222 return 1;
223 }
224 gdImagePng(im, out);
225 fclose(out);
226 gdImageDestroy(im);
227
228 return 0;
229 }
230
231 static int
write_base64_data(const unsigned char * data,unsigned int length,FILE * out)232 write_base64_data (const unsigned char *data, unsigned int length, FILE *out) {
233 base64s *b64;
234 int retval = 0;
235
236 b64 = gp_alloc(sizeof(base64s), "base64s");
237 if (b64 == NULL)
238 return 1;
239
240 init_base64_state_data (b64, out);
241
242 if (piecemeal_write_base64_data (data, length, b64) != 0)
243 retval = 1;
244 else
245 retval = piecemeal_write_base64_data_finish (b64);
246
247 free(b64);
248
249 return retval;
250 }
251
252 static int
write_png_base64_image(unsigned M,unsigned N,coordval * image,t_imagecolor color_mode,FILE * out)253 write_png_base64_image (unsigned M, unsigned N, coordval *image, t_imagecolor color_mode, FILE *out) {
254 gdImagePtr im;
255 void *pngdata;
256 int pngsize;
257 int retval = 0;
258
259 im = construct_gd_image (M, N, image, color_mode);
260 if (!im)
261 return 1;
262
263 if ((pngdata = gdImagePngPtr(im, &pngsize)) == NULL) {
264 gdImageDestroy(im);
265 return 1;
266 }
267
268 retval = write_base64_data (pngdata, pngsize, out);
269
270 gdFree(pngdata);
271 gdImageDestroy(im);
272
273 return retval;
274 }
275 #endif /* HAVE_CAIRO_PDF */
276 #endif /* WRITE_PNG_IMAGE */
277 #endif /* TERM_BODY */
278