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 static int
write_base64_data(const unsigned char * data,unsigned int length,FILE * out)80 write_base64_data (const unsigned char *data, unsigned int length, FILE *out) {
81 base64s *b64;
82 int retval = 0;
83
84 b64 = gp_alloc(sizeof(base64s), "base64s");
85 if (b64 == NULL)
86 return 1;
87
88 init_base64_state_data (b64, out);
89
90 if (piecemeal_write_base64_data (data, length, b64) != 0)
91 retval = 1;
92 else
93 retval = piecemeal_write_base64_data_finish (b64);
94
95 free(b64);
96
97 return retval;
98 }
99
100 #ifdef HAVE_CAIROPDF
101
102 #include "cairo-pdf.h"
103 #include "wxterminal/gp_cairo.h"
104 #include "wxterminal/gp_cairo_helpers.h"
105
106 /* cairo PNG code */
107 static int
write_png_image(unsigned m,unsigned n,coordval * image,t_imagecolor color_mode,const char * filename)108 write_png_image (unsigned m, unsigned n, coordval *image, t_imagecolor color_mode, const char *filename) {
109 cairo_surface_t *image_surface;
110 cairo_status_t cairo_stat;
111 unsigned int *image255;
112
113 image255 = gp_cairo_helper_coordval_to_chars(image, m, n, color_mode);
114 image_surface = cairo_image_surface_create_for_data((unsigned char*) image255, CAIRO_FORMAT_ARGB32, m, n, 4*m);
115 cairo_stat = cairo_surface_write_to_png(image_surface, filename);
116 cairo_surface_destroy(image_surface);
117 if (cairo_stat != CAIRO_STATUS_SUCCESS) {
118 int_warn(NO_CARET, "write_png_image cairo: could not write image file '%s': %s.", filename, cairo_status_to_string(cairo_stat));
119 return 1;
120 } else
121 return 0;
122 }
123
124 cairo_status_t
cairo_write_base64_callback(void * closure,const unsigned char * data,unsigned int length)125 cairo_write_base64_callback (void *closure, const unsigned char *data, unsigned int length) {
126 if (piecemeal_write_base64_data (data, length, closure) == 0)
127 return CAIRO_STATUS_SUCCESS;
128 else
129 return CAIRO_STATUS_WRITE_ERROR;
130 }
131
132 static int
write_png_base64_image(unsigned m,unsigned n,coordval * image,t_imagecolor color_mode,FILE * out)133 write_png_base64_image (unsigned m, unsigned n, coordval *image, t_imagecolor color_mode, FILE *out) {
134 cairo_surface_t *image_surface;
135 cairo_status_t cairo_stat;
136 unsigned int *image255;
137 base64s *b64;
138 int retval = 0;
139
140 b64 = gp_alloc(sizeof(base64s), "base64s");
141 if (b64 == NULL)
142 return 1;
143
144 image255 = gp_cairo_helper_coordval_to_chars(image, m, n, color_mode);
145 image_surface = cairo_image_surface_create_for_data((unsigned char*) image255, CAIRO_FORMAT_ARGB32, m, n, 4*m);
146
147 init_base64_state_data (b64, out);
148 cairo_stat = cairo_surface_write_to_png_stream(image_surface, cairo_write_base64_callback, b64);
149 cairo_surface_destroy(image_surface);
150 if (cairo_stat != CAIRO_STATUS_SUCCESS) {
151 int_warn(NO_CARET, "write_png_image cairo: could not write image file: %s.", cairo_status_to_string(cairo_stat));
152 retval = 1;
153 } else
154 retval = piecemeal_write_base64_data_finish (b64);
155
156 free(b64);
157
158 return retval;
159 }
160
161 #else /* libgd PNG code mainly taken from gd.trm */
162 #include <gd.h>
163
164 gdImagePtr
construct_gd_image(unsigned M,unsigned N,coordval * image,t_imagecolor color_mode)165 construct_gd_image (unsigned M, unsigned N, coordval *image, t_imagecolor color_mode) {
166 int m, n, pixel;
167 gdImagePtr im;
168
169 im = gdImageCreateTrueColor(M, N);
170 if (!im) {
171 int_warn(NO_CARET, "libgd: failed to create image structure");
172 return im;
173 }
174 /* gdImageColorAllocateAlpha(im, 255, 255, 255, 127); */
175 gdImageSaveAlpha(im, 1);
176 gdImageAlphaBlending(im, 0);
177
178 if (color_mode == IC_RGBA) {
179 /* RGB + Alpha channel */
180 for (n=0; n<N; n++) {
181 for (m=0; m<M; m++) {
182 rgb_color rgb1;
183 rgb255_color rgb255;
184 int alpha;
185 rgb1.r = *image++;
186 rgb1.g = *image++;
187 rgb1.b = *image++;
188 alpha = *image++;
189 alpha = 127 - (alpha>>1); /* input is [0:255] but gd wants [127:0] */
190 rgb255_from_rgb1( rgb1, &rgb255 );
191 pixel = gdImageColorResolveAlpha(im, (int)rgb255.r, (int)rgb255.g, (int)rgb255.b, alpha);
192 gdImageSetPixel( im, m, n, pixel );
193 }
194 }
195 } else if (color_mode == IC_RGB) {
196 /* TrueColor 24-bit color mode */
197 for (n=0; n<N; n++) {
198 for (m=0; m<M; m++) {
199 rgb_color rgb1;
200 rgb255_color rgb255;
201 rgb1.r = *image++;
202 rgb1.g = *image++;
203 rgb1.b = *image++;
204 rgb255_from_rgb1( rgb1, &rgb255 );
205 pixel = gdImageColorResolve(im, (int)rgb255.r, (int)rgb255.g, (int)rgb255.b );
206 gdImageSetPixel( im, m, n, pixel );
207 }
208 }
209 } else if (color_mode == IC_PALETTE) {
210 /* Palette color lookup from gray value */
211 for (n=0; n<N; n++) {
212 for (m=0; m<M; m++) {
213 rgb255_color rgb;
214 if (isnan(*image)) {
215 /* FIXME: tried to take the comment from gd.trm into account but needs a testcase */
216 pixel = gdImageColorResolveAlpha(im, 0, 0, 0, 127);
217 image++;
218 } else {
219 rgb255maxcolors_from_gray( *image++, &rgb );
220 pixel = gdImageColorResolve( im, (int)rgb.r, (int)rgb.g, (int)rgb.b );
221 }
222 gdImageSetPixel( im, m, n, pixel );
223 }
224 }
225 }
226
227 return im;
228 }
229
230 static int
write_png_image(unsigned M,unsigned N,coordval * image,t_imagecolor color_mode,const char * filename)231 write_png_image (unsigned M, unsigned N, coordval *image, t_imagecolor color_mode, const char *filename) {
232 gdImagePtr im;
233 FILE *out;
234
235 im = construct_gd_image (M, N, image, color_mode);
236 if (!im)
237 return 1;
238
239 out = fopen(filename, "wb");
240 if (!out) {
241 int_warn(NO_CARET, "write_png_image libgd: could not write image file '%s'", filename);
242 gdImageDestroy(im);
243 return 1;
244 }
245 gdImagePng(im, out);
246 fclose(out);
247 gdImageDestroy(im);
248
249 return 0;
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
276 #endif
277 #endif
278