]> granicus.if.org Git - imagemagick/blob - coders/sixel.c
a0225c6a9786e6342f0f52921d031bc3cf7f167a
[imagemagick] / coders / sixel.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %                     SSSSS  IIIII  X   X  EEEEE  L                           %
7 %                     SS       I     X X   E      L                           %
8 %                      SSS     I      X    EEE    L                           %
9 %                        SS    I     X X   E      L                           %
10 %                     SSSSS  IIIII  X   X  EEEEE  LLLLL                       %
11 %                                                                             %
12 %                                                                             %
13 %                        Read/Write DEC SIXEL Format                          %
14 %                                                                             %
15 %                              Software Design                                %
16 %                               Hayaki Saito                                  %
17 %                              September 2014                                 %
18 %                    Based on kmiya's sixel (2014-03-28)                      %
19 %                                                                             %
20 %                                                                             %
21 %  Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization      %
22 %  dedicated to making software imaging solutions freely available.           %
23 %                                                                             %
24 %  You may not use this file except in compliance with the License.  You may  %
25 %  obtain a copy of the License at                                            %
26 %                                                                             %
27 %    http://www.imagemagick.org/script/license.php                            %
28 %                                                                             %
29 %  Unless required by applicable law or agreed to in writing, software        %
30 %  distributed under the License is distributed on an "AS IS" BASIS,          %
31 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
32 %  See the License for the specific language governing permissions and        %
33 %  limitations under the License.                                             %
34 %                                                                             %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 %
37 %
38 */
39 \f
40 /*
41   Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/attribute.h"
45 #include "MagickCore/blob.h"
46 #include "MagickCore/blob-private.h"
47 #include "MagickCore/cache.h"
48 #include "MagickCore/color.h"
49 #include "MagickCore/color-private.h"
50 #include "MagickCore/colormap.h"
51 #include "MagickCore/colorspace.h"
52 #include "MagickCore/colorspace-private.h"
53 #include "MagickCore/exception.h"
54 #include "MagickCore/exception-private.h"
55 #include "MagickCore/geometry.h"
56 #include "MagickCore/image.h"
57 #include "MagickCore/image-private.h"
58 #include "MagickCore/list.h"
59 #include "MagickCore/magick.h"
60 #include "MagickCore/memory_.h"
61 #include "MagickCore/monitor.h"
62 #include "MagickCore/monitor-private.h"
63 #include "MagickCore/pixel-accessor.h"
64 #include "MagickCore/pixel-private.h"
65 #include "MagickCore/quantize.h"
66 #include "MagickCore/quantum-private.h"
67 #include "MagickCore/resize.h"
68 #include "MagickCore/resource_.h"
69 #include "MagickCore/splay-tree.h"
70 #include "MagickCore/static.h"
71 #include "MagickCore/string_.h"
72 #include "MagickCore/thread-private.h"
73 #include "MagickCore/module.h"
74 #include "MagickCore/threshold.h"
75 #include "MagickCore/utility.h"
76
77 /*
78   Definitions
79 */
80 #define SIXEL_PALETTE_MAX 256
81 #define SIXEL_OUTPUT_PACKET_SIZE 1024
82
83 /*
84   Macros
85 */
86 #define SIXEL_RGB(r, g, b) (((r) << 16) + ((g) << 8) +  (b))
87 #define SIXEL_PALVAL(n,a,m) (((n) * (a) + ((m) / 2)) / (m))
88 #define SIXEL_XRGB(r,g,b) SIXEL_RGB(SIXEL_PALVAL(r, 255, 100), SIXEL_PALVAL(g, 255, 100), SIXEL_PALVAL(b, 255, 100))
89
90 /*
91   Structure declarations.
92 */
93 typedef struct sixel_node {
94     struct sixel_node *next;
95     int color;
96     int left;
97     int right;
98     unsigned char *map;
99 } sixel_node_t;
100
101 typedef struct sixel_output {
102
103     /* compatiblity flags */
104
105     /* 0: 7bit terminal,
106      * 1: 8bit terminal */
107     unsigned char has_8bit_control;
108
109     int save_pixel;
110     int save_count;
111     int active_palette;
112
113     sixel_node_t *node_top;
114     sixel_node_t *node_free;
115
116     Image *image;
117     int pos;
118     unsigned char buffer[1];
119
120 } sixel_output_t;
121
122 static int const sixel_default_color_table[] = {
123     SIXEL_XRGB(0,  0,  0),   /*  0 Black    */
124     SIXEL_XRGB(20, 20, 80),  /*  1 Blue     */
125     SIXEL_XRGB(80, 13, 13),  /*  2 Red      */
126     SIXEL_XRGB(20, 80, 20),  /*  3 Green    */
127     SIXEL_XRGB(80, 20, 80),  /*  4 Magenta  */
128     SIXEL_XRGB(20, 80, 80),  /*  5 Cyan     */
129     SIXEL_XRGB(80, 80, 20),  /*  6 Yellow   */
130     SIXEL_XRGB(53, 53, 53),  /*  7 Gray 50% */
131     SIXEL_XRGB(26, 26, 26),  /*  8 Gray 25% */
132     SIXEL_XRGB(33, 33, 60),  /*  9 Blue*    */
133     SIXEL_XRGB(60, 26, 26),  /* 10 Red*     */
134     SIXEL_XRGB(33, 60, 33),  /* 11 Green*   */
135     SIXEL_XRGB(60, 33, 60),  /* 12 Magenta* */
136     SIXEL_XRGB(33, 60, 60),  /* 13 Cyan*    */
137     SIXEL_XRGB(60, 60, 33),  /* 14 Yellow*  */
138     SIXEL_XRGB(80, 80, 80),  /* 15 Gray 75% */
139 };
140
141 /*
142   Forward declarations.
143 */
144 static MagickBooleanType
145   WriteSIXELImage(const ImageInfo *,Image *,ExceptionInfo *);
146 \f
147 static int hue_to_rgb(int n1, int n2, int hue)
148 {
149     const int HLSMAX = 100;
150
151     if (hue < 0) {
152         hue += HLSMAX;
153     }
154
155     if (hue > HLSMAX) {
156         hue -= HLSMAX;
157     }
158
159     if (hue < (HLSMAX / 6)) {
160         return (n1 + (((n2 - n1) * hue + (HLSMAX / 12)) / (HLSMAX / 6)));
161     }
162     if (hue < (HLSMAX / 2)) {
163         return (n2);
164     }
165     if (hue < ((HLSMAX * 2) / 3)) {
166         return (n1 + (((n2 - n1) * (((HLSMAX * 2) / 3) - hue) + (HLSMAX / 12))/(HLSMAX / 6)));
167     }
168     return (n1);
169 }
170
171 static int hls_to_rgb(int hue, int lum, int sat)
172 {
173     int R, G, B;
174     int Magic1, Magic2;
175     const int RGBMAX = 255;
176     const int HLSMAX = 100;
177
178     if (sat == 0) {
179         R = G = B = (lum * RGBMAX) / HLSMAX;
180     } else {
181         if (lum <= (HLSMAX / 2)) {
182             Magic2 = (lum * (HLSMAX + sat) + (HLSMAX / 2)) / HLSMAX;
183         } else {
184             Magic2 = lum + sat - ((lum * sat) + (HLSMAX / 2)) / HLSMAX;
185         }
186         Magic1 = 2 * lum - Magic2;
187
188         R = (hue_to_rgb(Magic1, Magic2, hue + (HLSMAX / 3)) * RGBMAX + (HLSMAX / 2)) / HLSMAX;
189         G = (hue_to_rgb(Magic1, Magic2, hue) * RGBMAX + (HLSMAX / 2)) / HLSMAX;
190         B = (hue_to_rgb(Magic1, Magic2, hue - (HLSMAX / 3)) * RGBMAX + (HLSMAX/2)) / HLSMAX;
191     }
192     return SIXEL_RGB(R, G, B);
193 }
194
195 static unsigned char *get_params(unsigned char *p, int *param, int *len)
196 {
197     int n;
198
199     *len = 0;
200     while (*p != '\0') {
201         while (*p == ' ' || *p == '\t') {
202             p++;
203         }
204         if (isdigit(*p)) {
205             for (n = 0; isdigit(*p); p++) {
206                 n = n * 10 + (*p - '0');
207             }
208             if (*len < 10) {
209                 param[(*len)++] = n;
210             }
211             while (*p == ' ' || *p == '\t') {
212                 p++;
213             }
214             if (*p == ';') {
215                 p++;
216             }
217         } else if (*p == ';') {
218             if (*len < 10) {
219                 param[(*len)++] = 0;
220             }
221             p++;
222         } else
223             break;
224     }
225     return p;
226 }
227
228 /* convert sixel data into indexed pixel bytes and palette data */
229 MagickBooleanType sixel_decode(unsigned char              /* in */  *p,         /* sixel bytes */
230                                unsigned char              /* out */ **pixels,   /* decoded pixels */
231                                size_t                     /* out */ *pwidth,    /* image width */
232                                size_t                     /* out */ *pheight,   /* image height */
233                                unsigned char              /* out */ **palette,  /* ARGB palette */
234                                size_t                     /* out */ *ncolors    /* palette size (<= 256) */)
235 {
236     int n, i, r, g, b, sixel_vertical_mask, c;
237     int posision_x, posision_y;
238     int max_x, max_y;
239     int attributed_pan, attributed_pad;
240     int attributed_ph, attributed_pv;
241     int repeat_count, color_index, max_color_index = 2, background_color_index;
242     int param[10];
243     int sixel_palet[SIXEL_PALETTE_MAX];
244     unsigned char *imbuf, *dmbuf;
245     int imsx, imsy;
246     int dmsx, dmsy;
247     int y;
248
249     posision_x = posision_y = 0;
250     max_x = max_y = 0;
251     attributed_pan = 2;
252     attributed_pad = 1;
253     attributed_ph = attributed_pv = 0;
254     repeat_count = 1;
255     color_index = 0;
256     background_color_index = 0;
257
258     imsx = 2048;
259     imsy = 2048;
260     imbuf = (unsigned char *) AcquireQuantumMemory(imsx * imsy,1);
261
262     if (imbuf == NULL) {
263         return(MagickFalse);
264     }
265
266     for (n = 0; n < 16; n++) {
267         sixel_palet[n] = sixel_default_color_table[n];
268     }
269
270     /* colors 16-231 are a 6x6x6 color cube */
271     for (r = 0; r < 6; r++) {
272         for (g = 0; g < 6; g++) {
273             for (b = 0; b < 6; b++) {
274                 sixel_palet[n++] = SIXEL_RGB(r * 51, g * 51, b * 51);
275             }
276         }
277     }
278     /* colors 232-255 are a grayscale ramp, intentionally leaving out */
279     for (i = 0; i < 24; i++) {
280         sixel_palet[n++] = SIXEL_RGB(i * 11, i * 11, i * 11);
281     }
282
283     for (; n < SIXEL_PALETTE_MAX; n++) {
284         sixel_palet[n] = SIXEL_RGB(255, 255, 255);
285     }
286
287     (void) ResetMagickMemory(imbuf, background_color_index, imsx * imsy);
288
289     while (*p != '\0') {
290         if ((p[0] == '\033' && p[1] == 'P') || *p == 0x90) {
291             if (*p == '\033') {
292                 p++;
293             }
294
295             p = get_params(++p, param, &n);
296
297             if (*p == 'q') {
298                 p++;
299
300                 if (n > 0) {        /* Pn1 */
301                     switch(param[0]) {
302                     case 0:
303                     case 1:
304                         attributed_pad = 2;
305                         break;
306                     case 2:
307                         attributed_pad = 5;
308                         break;
309                     case 3:
310                         attributed_pad = 4;
311                         break;
312                     case 4:
313                         attributed_pad = 4;
314                         break;
315                     case 5:
316                         attributed_pad = 3;
317                         break;
318                     case 6:
319                         attributed_pad = 3;
320                         break;
321                     case 7:
322                         attributed_pad = 2;
323                         break;
324                     case 8:
325                         attributed_pad = 2;
326                         break;
327                     case 9:
328                         attributed_pad = 1;
329                         break;
330                     }
331                 }
332
333                 if (n > 2) {        /* Pn3 */
334                     if (param[2] == 0) {
335                         param[2] = 10;
336                     }
337                     attributed_pan = attributed_pan * param[2] / 10;
338                     attributed_pad = attributed_pad * param[2] / 10;
339                     if (attributed_pan <= 0) attributed_pan = 1;
340                     if (attributed_pad <= 0) attributed_pad = 1;
341                 }
342             }
343
344         } else if ((p[0] == '\033' && p[1] == '\\') || *p == 0x9C) {
345             break;
346         } else if (*p == '"') {
347             /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */
348             p = get_params(++p, param, &n);
349
350             if (n > 0) attributed_pad = param[0];
351             if (n > 1) attributed_pan = param[1];
352             if (n > 2 && param[2] > 0) attributed_ph = param[2];
353             if (n > 3 && param[3] > 0) attributed_pv = param[3];
354
355             if (attributed_pan <= 0) attributed_pan = 1;
356             if (attributed_pad <= 0) attributed_pad = 1;
357
358             if (imsx < attributed_ph || imsy < attributed_pv) {
359                 dmsx = imsx > attributed_ph ? imsx : attributed_ph;
360                 dmsy = imsy > attributed_pv ? imsy : attributed_pv;
361                 dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx * dmsy,1);
362                 if (dmbuf == (unsigned char *) NULL) {
363                     imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
364                     return (MagickFalse);
365                 }
366                 (void) ResetMagickMemory(dmbuf, background_color_index, dmsx * dmsy);
367                 for (y = 0; y < imsy; ++y) {
368                     (void) CopyMagickMemory(dmbuf + dmsx * y, imbuf + imsx * y, imsx);
369                 }
370                 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
371                 imsx = dmsx;
372                 imsy = dmsy;
373                 imbuf = dmbuf;
374             }
375
376         } else if (*p == '!') {
377             /* DECGRI Graphics Repeat Introducer ! Pn Ch */
378             p = get_params(++p, param, &n);
379
380             if (n > 0) {
381                 repeat_count = param[0];
382             }
383
384         } else if (*p == '#') {
385             /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */
386             p = get_params(++p, param, &n);
387
388             if (n > 0) {
389                 if ((color_index = param[0]) < 0) {
390                     color_index = 0;
391                 } else if (color_index >= SIXEL_PALETTE_MAX) {
392                     color_index = SIXEL_PALETTE_MAX - 1;
393                 }
394             }
395
396             if (n > 4) {
397                 if (param[1] == 1) {            /* HLS */
398                     if (param[2] > 360) param[2] = 360;
399                     if (param[3] > 100) param[3] = 100;
400                     if (param[4] > 100) param[4] = 100;
401                     sixel_palet[color_index] = hls_to_rgb(param[2] * 100 / 360, param[3], param[4]);
402                 } else if (param[1] == 2) {    /* RGB */
403                     if (param[2] > 100) param[2] = 100;
404                     if (param[3] > 100) param[3] = 100;
405                     if (param[4] > 100) param[4] = 100;
406                     sixel_palet[color_index] = SIXEL_XRGB(param[2], param[3], param[4]);
407                 }
408             }
409
410         } else if (*p == '$') {
411             /* DECGCR Graphics Carriage Return */
412             p++;
413             posision_x = 0;
414             repeat_count = 1;
415
416         } else if (*p == '-') {
417             /* DECGNL Graphics Next Line */
418             p++;
419             posision_x  = 0;
420             posision_y += 6;
421             repeat_count = 1;
422
423         } else if (*p >= '?' && *p <= '\177') {
424             if (imsx < (posision_x + repeat_count) || imsy < (posision_y + 6)) {
425                 int nx = imsx * 2;
426                 int ny = imsy * 2;
427
428                 while (nx < (posision_x + repeat_count) || ny < (posision_y + 6)) {
429                     nx *= 2;
430                     ny *= 2;
431                 }
432
433                 dmsx = nx;
434                 dmsy = ny;
435                 dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx * dmsy,1);
436                 if (dmbuf == (unsigned char *) NULL) {
437                     imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
438                     return (MagickFalse);
439                 }
440                 (void) ResetMagickMemory(dmbuf, background_color_index, dmsx * dmsy);
441                 for (y = 0; y < imsy; ++y) {
442                     (void) CopyMagickMemory(dmbuf + dmsx * y, imbuf + imsx * y, imsx);
443                 }
444                 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
445                 imsx = dmsx;
446                 imsy = dmsy;
447                 imbuf = dmbuf;
448             }
449
450             if (color_index > max_color_index) {
451                 max_color_index = color_index;
452             }
453             if ((b = *(p++) - '?') == 0) {
454                 posision_x += repeat_count;
455
456             } else {
457                 sixel_vertical_mask = 0x01;
458
459                 if (repeat_count <= 1) {
460                     for (i = 0; i < 6; i++) {
461                         if ((b & sixel_vertical_mask) != 0) {
462                             imbuf[imsx * (posision_y + i) + posision_x] = color_index;
463                             if (max_x < posision_x) {
464                                 max_x = posision_x;
465                             }
466                             if (max_y < (posision_y + i)) {
467                                 max_y = posision_y + i;
468                             }
469                         }
470                         sixel_vertical_mask <<= 1;
471                     }
472                     posision_x += 1;
473
474                 } else { /* repeat_count > 1 */
475                     for (i = 0; i < 6; i++) {
476                         if ((b & sixel_vertical_mask) != 0) {
477                             c = sixel_vertical_mask << 1;
478                             for (n = 1; (i + n) < 6; n++) {
479                                 if ((b & c) == 0) {
480                                     break;
481                                 }
482                                 c <<= 1;
483                             }
484                             for (y = posision_y + i; y < posision_y + i + n; ++y) {
485                                 (void) ResetMagickMemory(imbuf + imsx * y + posision_x, color_index, repeat_count);
486                             }
487                             if (max_x < (posision_x + repeat_count - 1)) {
488                                 max_x = posision_x + repeat_count - 1;
489                             }
490                             if (max_y < (posision_y + i + n - 1)) {
491                                 max_y = posision_y + i + n - 1;
492                             }
493
494                             i += (n - 1);
495                             sixel_vertical_mask <<= (n - 1);
496                         }
497                         sixel_vertical_mask <<= 1;
498                     }
499                     posision_x += repeat_count;
500                 }
501             }
502             repeat_count = 1;
503         } else {
504             p++;
505         }
506     }
507
508     if (++max_x < attributed_ph) {
509         max_x = attributed_ph;
510     }
511     if (++max_y < attributed_pv) {
512         max_y = attributed_pv;
513     }
514
515     if (imsx > max_x || imsy > max_y) {
516         dmsx = max_x;
517         dmsy = max_y;
518         if ((dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx * dmsy,1)) == NULL) {
519             imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
520             return (MagickFalse);
521         }
522         for (y = 0; y < dmsy; ++y) {
523             (void) CopyMagickMemory(dmbuf + dmsx * y, imbuf + imsx * y, dmsx);
524         }
525         imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
526         imsx = dmsx;
527         imsy = dmsy;
528         imbuf = dmbuf;
529     }
530
531     *pixels = imbuf;
532     *pwidth = imsx;
533     *pheight = imsy;
534     *ncolors = max_color_index + 1;
535     *palette = (unsigned char *) AcquireQuantumMemory(*ncolors,4);
536     for (n = 0; n < (ssize_t) *ncolors; ++n) {
537         (*palette)[n * 4 + 0] = sixel_palet[n] >> 16 & 0xff;
538         (*palette)[n * 4 + 1] = sixel_palet[n] >> 8 & 0xff;
539         (*palette)[n * 4 + 2] = sixel_palet[n] & 0xff;
540         (*palette)[n * 4 + 3] = 0xff;
541     }
542     return(MagickTrue);
543 }
544
545 sixel_output_t *sixel_output_create(Image *image)
546 {
547     sixel_output_t *output;
548
549     output = (sixel_output_t *) AcquireQuantumMemory(sizeof(sixel_output_t) + SIXEL_OUTPUT_PACKET_SIZE * 2, 1);
550     output->has_8bit_control = 0;
551     output->save_pixel = 0;
552     output->save_count = 0;
553     output->active_palette = (-1);
554     output->node_top = NULL;
555     output->node_free = NULL;
556     output->image = image;
557     output->pos = 0;
558
559     return output;
560 }
561
562 static void sixel_advance(sixel_output_t *context, int nwrite)
563 {
564     if ((context->pos += nwrite) >= SIXEL_OUTPUT_PACKET_SIZE) {
565         WriteBlob(context->image,SIXEL_OUTPUT_PACKET_SIZE,context->buffer);
566         CopyMagickMemory(context->buffer,
567                context->buffer + SIXEL_OUTPUT_PACKET_SIZE,
568                (context->pos -= SIXEL_OUTPUT_PACKET_SIZE));
569     }
570 }
571
572 static int sixel_put_flash(sixel_output_t *const context)
573 {
574     int n;
575     int nwrite;
576
577 #if defined(USE_VT240)        /* VT240 Max 255 ? */
578     while (context->save_count > 255) {
579         nwrite = spritf((char *)context->buffer + context->pos, "!255%c", context->save_pixel);
580         if (nwrite <= 0) {
581             return (-1);
582         }
583         sixel_advance(context, nwrite);
584         context->save_count -= 255;
585     }
586 #endif  /* defined(USE_VT240) */
587
588     if (context->save_count > 3) {
589         /* DECGRI Graphics Repeat Introducer ! Pn Ch */
590         nwrite = sprintf((char *)context->buffer + context->pos, "!%d%c", context->save_count, context->save_pixel);
591         if (nwrite <= 0) {
592             return (-1);
593         }
594         sixel_advance(context, nwrite);
595     } else {
596         for (n = 0; n < context->save_count; n++) {
597             context->buffer[context->pos] = (char)context->save_pixel;
598             sixel_advance(context, 1);
599         }
600     }
601
602     context->save_pixel = 0;
603     context->save_count = 0;
604
605     return 0;
606 }
607
608 static void sixel_put_pixel(sixel_output_t *const context, int pix)
609 {
610     if (pix < 0 || pix > '?') {
611         pix = 0;
612     }
613
614     pix += '?';
615
616     if (pix == context->save_pixel) {
617         context->save_count++;
618     } else {
619         sixel_put_flash(context);
620         context->save_pixel = pix;
621         context->save_count = 1;
622     }
623 }
624
625 static void sixel_node_del(sixel_output_t *const context, sixel_node_t *np)
626 {
627     sixel_node_t *tp;
628
629     if ((tp = context->node_top) == np) {
630         context->node_top = np->next;
631     }
632
633     else {
634         while (tp->next != NULL) {
635             if (tp->next == np) {
636                 tp->next = np->next;
637                 break;
638             }
639             tp = tp->next;
640         }
641     }
642
643     np->next = context->node_free;
644     context->node_free = np;
645 }
646
647 static int sixel_put_node(sixel_output_t *const context, int x,
648                sixel_node_t *np, int ncolors, int keycolor)
649 {
650     int nwrite;
651
652     if (ncolors != 2 || keycolor == -1) {
653         /* designate palette index */
654         if (context->active_palette != np->color) {
655             nwrite = sprintf((char *)context->buffer + context->pos,
656                              "#%d", np->color);
657             sixel_advance(context, nwrite);
658             context->active_palette = np->color;
659         }
660     }
661
662     for (; x < np->left; x++) {
663         sixel_put_pixel(context, 0);
664     }
665
666     for (; x < np->right; x++) {
667         sixel_put_pixel(context, np->map[x]);
668     }
669
670     sixel_put_flash(context);
671
672     return x;
673 }
674
675 static MagickBooleanType sixel_encode_impl(unsigned char *pixels, size_t width,size_t height,
676                   unsigned char *palette, size_t ncolors, int keycolor,
677                   sixel_output_t *context)
678 {
679 #define RelinquishNodesAndMap \
680     while ((np = context->node_free) != NULL) { \
681         context->node_free = np->next; \
682         np=(sixel_node_t *) RelinquishMagickMemory(np); \
683     } \
684     map = (unsigned char *) RelinquishMagickMemory(map)
685
686     int x, y, i, n, c;
687     int left, right;
688     int len, pix;
689     unsigned char *map;
690     sixel_node_t *np, *tp, top;
691     int nwrite;
692
693     context->pos = 0;
694
695     if (ncolors < 1) {
696         return (MagickFalse);
697     }
698     len = ncolors * width;
699     context->active_palette = (-1);
700
701     if ((map = (unsigned char *)AcquireQuantumMemory(len, sizeof(unsigned char))) == NULL) {
702         return (MagickFalse);
703     }
704     (void) ResetMagickMemory(map, 0, len);
705
706     if (context->has_8bit_control) {
707         nwrite = sprintf((char *)context->buffer, "\x90" "0;0;0" "q");
708     } else {
709         nwrite = sprintf((char *)context->buffer, "\x1bP" "0;0;0" "q");
710     }
711     if (nwrite <= 0) {
712         return (MagickFalse);
713     }
714     sixel_advance(context, nwrite);
715     nwrite = sprintf((char *)context->buffer + context->pos, "\"1;1;%d;%d", (int) width, (int) height);
716     if (nwrite <= 0) {
717         RelinquishNodesAndMap;
718         return (MagickFalse);
719     }
720     sixel_advance(context, nwrite);
721
722     if (ncolors != 2 || keycolor == -1) {
723         for (n = 0; n < (ssize_t) ncolors; n++) {
724             /* DECGCI Graphics Color Introducer  # Pc ; Pu; Px; Py; Pz */
725             nwrite = sprintf((char *)context->buffer + context->pos, "#%d;2;%d;%d;%d",
726                              n,
727                              (palette[n * 3 + 0] * 100 + 127) / 255,
728                              (palette[n * 3 + 1] * 100 + 127) / 255,
729                              (palette[n * 3 + 2] * 100 + 127) / 255);
730             if (nwrite <= 0) {
731                 RelinquishNodesAndMap;
732                 return (MagickFalse);
733             }
734             sixel_advance(context, nwrite);
735             if (nwrite <= 0) {
736                 RelinquishNodesAndMap;
737                 return (MagickFalse);
738             }
739         }
740     }
741
742     for (y = i = 0; y < (ssize_t) height; y++) {
743         for (x = 0; x < (ssize_t) width; x++) {
744             pix = pixels[y * width + x];
745             if (pix >= 0 && pix < (ssize_t) ncolors && pix != keycolor) {
746                 map[pix * width + x] |= (1 << i);
747             }
748         }
749
750         if (++i < 6 && (y + 1) < (ssize_t) height) {
751             continue;
752         }
753
754         for (c = 0; c < (ssize_t) ncolors; c++) {
755             for (left = 0; left < (ssize_t) width; left++) {
756                 if (*(map + c * width + left) == 0) {
757                     continue;
758                 }
759
760                 for (right = left + 1; right < (ssize_t) width; right++) {
761                     if (*(map + c * width + right) != 0) {
762                         continue;
763                     }
764
765                     for (n = 1; (right + n) < (ssize_t) width; n++) {
766                         if (*(map + c * width + right + n) != 0) {
767                             break;
768                         }
769                     }
770
771                     if (n >= 10 || right + n >= (ssize_t) width) {
772                         break;
773                     }
774                     right = right + n - 1;
775                 }
776
777                 if ((np = context->node_free) != NULL) {
778                     context->node_free = np->next;
779                 } else if ((np = (sixel_node_t *)AcquireMagickMemory(sizeof(sixel_node_t))) == NULL) {
780                     RelinquishNodesAndMap;
781                     return (MagickFalse);
782                 }
783
784                 np->color = c;
785                 np->left = left;
786                 np->right = right;
787                 np->map = map + c * width;
788
789                 top.next = context->node_top;
790                 tp = &top;
791
792                 while (tp->next != NULL) {
793                     if (np->left < tp->next->left) {
794                         break;
795                     }
796                     if (np->left == tp->next->left && np->right > tp->next->right) {
797                         break;
798                     }
799                     tp = tp->next;
800                 }
801
802                 np->next = tp->next;
803                 tp->next = np;
804                 context->node_top = top.next;
805
806                 left = right - 1;
807             }
808
809         }
810
811         for (x = 0; (np = context->node_top) != NULL;) {
812             if (x > np->left) {
813                 /* DECGCR Graphics Carriage Return */
814                 context->buffer[context->pos] = '$';
815                 sixel_advance(context, 1);
816                 x = 0;
817             }
818
819             x = sixel_put_node(context, x, np, ncolors, keycolor);
820             sixel_node_del(context, np);
821             np = context->node_top;
822
823             while (np != NULL) {
824                 if (np->left < x) {
825                     np = np->next;
826                     continue;
827                 }
828
829                 x = sixel_put_node(context, x, np, ncolors, keycolor);
830                 sixel_node_del(context, np);
831                 np = context->node_top;
832             }
833         }
834
835         /* DECGNL Graphics Next Line */
836         context->buffer[context->pos] = '-';
837         sixel_advance(context, 1);
838         if (nwrite <= 0) {
839             RelinquishNodesAndMap;
840             return (MagickFalse);
841         }
842
843         i = 0;
844         (void) ResetMagickMemory(map, 0, len);
845     }
846
847     if (context->has_8bit_control) {
848         context->buffer[context->pos] = 0x9c;
849         sixel_advance(context, 1);
850     } else {
851         context->buffer[context->pos] = 0x1b;
852         context->buffer[context->pos + 1] = '\\';
853         sixel_advance(context, 2);
854     }
855     if (nwrite <= 0) {
856         RelinquishNodesAndMap;
857         return (MagickFalse);
858     }
859
860     /* flush buffer */
861     if (context->pos > 0) {
862         WriteBlob(context->image,context->pos,context->buffer);
863     }
864
865     RelinquishNodesAndMap;
866
867     return(MagickTrue);
868 }
869 \f
870 /*
871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
872 %                                                                             %
873 %                                                                             %
874 %                                                                             %
875 %   I s S I X E L                                                             %
876 %                                                                             %
877 %                                                                             %
878 %                                                                             %
879 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
880 %
881 %  IsSIXEL() returns MagickTrue if the image format type, identified by the
882 %  magick string, is SIXEL.
883 %
884 %  The format of the IsSIXEL method is:
885 %
886 %      MagickBooleanType IsSIXEL(const unsigned char *magick,const size_t length)
887 %
888 %  A description of each parameter follows:
889 %
890 %    o magick: compare image format pattern against these bytes. or
891 %      blob.
892 %
893 %    o length: Specifies the length of the magick string.
894 %
895 */
896 static MagickBooleanType IsSIXEL(const unsigned char *magick,const size_t length)
897 {
898   const unsigned char
899     *end = magick + length;
900
901   if (length < 3)
902     return(MagickFalse);
903
904   if (*magick == 0x90 || (*magick == 0x1b && *++magick == 'P')) {
905     while (++magick != end) {
906       if (*magick == 'q')
907         return(MagickTrue);
908       if (!(*magick >= '0' && *magick <= '9') && *magick != ';')
909         return(MagickFalse);
910     }
911   }
912   return(MagickFalse);
913 }
914 \f
915 /*
916 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
917 %                                                                             %
918 %                                                                             %
919 %                                                                             %
920 %   R e a d S I X E L I m a g e                                               %
921 %                                                                             %
922 %                                                                             %
923 %                                                                             %
924 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
925 %
926 %  ReadSIXELImage() reads an X11 pixmap image file and returns it.  It
927 %  allocates the memory necessary for the new Image structure and returns a
928 %  pointer to the new image.
929 %
930 %  The format of the ReadSIXELImage method is:
931 %
932 %      Image *ReadSIXELImage(const ImageInfo *image_info,ExceptionInfo *exception)
933 %
934 %  A description of each parameter follows:
935 %
936 %    o image_info: the image info.
937 %
938 %    o exception: return any errors or warnings in this structure.
939 %
940 */
941 static Image *ReadSIXELImage(const ImageInfo *image_info,ExceptionInfo *exception)
942 {
943   char
944     *sixel_buffer;
945
946   Image
947     *image;
948
949   MagickBooleanType
950     status;
951
952   register char
953     *p;
954
955   register ssize_t
956     x;
957
958   register Quantum
959     *q;
960
961   size_t
962     length;
963
964   ssize_t
965     i,
966     j,
967     y;
968
969   unsigned char
970     *sixel_pixels,
971     *sixel_palette;
972
973   /*
974     Open image file.
975   */
976   assert(image_info != (const ImageInfo *) NULL);
977   assert(image_info->signature == MagickSignature);
978   if (image_info->debug != MagickFalse)
979     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
980       image_info->filename);
981   assert(exception != (ExceptionInfo *) NULL);
982   assert(exception->signature == MagickSignature);
983   image=AcquireImage(image_info,exception);
984   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
985   if (status == MagickFalse)
986     {
987       image=DestroyImageList(image);
988       return((Image *) NULL);
989     }
990   /*
991     Read SIXEL file.
992   */
993   length=MaxTextExtent;
994   sixel_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*sixel_buffer));
995   p=sixel_buffer;
996   if (sixel_buffer != (char *) NULL)
997     while (ReadBlobString(image,p) != (char *) NULL)
998     {
999       if ((*p == '#') && ((p == sixel_buffer) || (*(p-1) == '\n')))
1000         continue;
1001       if ((*p == '}') && (*(p+1) == ';'))
1002         break;
1003       p+=strlen(p);
1004       if ((size_t) (p-sixel_buffer+MaxTextExtent) < length)
1005         continue;
1006       length<<=1;
1007       sixel_buffer=(char *) ResizeQuantumMemory(sixel_buffer,length+MaxTextExtent,
1008         sizeof(*sixel_buffer));
1009       if (sixel_buffer == (char *) NULL)
1010         break;
1011       p=sixel_buffer+strlen(sixel_buffer);
1012     }
1013   if (sixel_buffer == (char *) NULL)
1014     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1015
1016   /*
1017     Decode SIXEL
1018   */
1019   if (sixel_decode((unsigned char *)sixel_buffer, &sixel_pixels, &image->columns, &image->rows, &sixel_palette, &image->colors) == MagickFalse)
1020     {
1021       sixel_buffer=(char *) RelinquishMagickMemory(sixel_buffer);
1022       ThrowReaderException(CorruptImageError,"CorruptImage");
1023     }
1024   sixel_buffer=(char *) RelinquishMagickMemory(sixel_buffer);
1025   image->depth=24;
1026   image->storage_class=PseudoClass;
1027
1028   if (AcquireImageColormap(image,image->colors, exception) == MagickFalse)
1029     {
1030       sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1031       sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
1032       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1033     }
1034   for (i = 0; i < (ssize_t) image->colors; ++i) {
1035     image->colormap[i].red   = ScaleCharToQuantum(sixel_palette[i * 4 + 0]);
1036     image->colormap[i].green = ScaleCharToQuantum(sixel_palette[i * 4 + 1]);
1037     image->colormap[i].blue  = ScaleCharToQuantum(sixel_palette[i * 4 + 2]);
1038   }
1039
1040   j=0;
1041   if (image_info->ping == MagickFalse)
1042     {
1043       /*
1044         Read image pixels.
1045       */
1046       for (y=0; y < (ssize_t) image->rows; y++)
1047       {
1048         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
1049         if (q == (Quantum *) NULL)
1050           break;
1051         for (x=0; x < (ssize_t) image->columns; x++)
1052         {
1053           j=(ssize_t) sixel_pixels[y * image->columns + x];
1054           SetPixelIndex(image,j,q);
1055           q+=GetPixelChannels(image);
1056         }
1057         if (SyncAuthenticPixels(image,exception) == MagickFalse)
1058           break;
1059       }
1060       if (y < (ssize_t) image->rows)
1061         {
1062           sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1063           sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
1064           ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
1065         }
1066     }
1067   /*
1068     Relinquish resources.
1069   */
1070   sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1071   sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
1072   (void) CloseBlob(image);
1073   return(GetFirstImageInList(image));
1074 }
1075 \f
1076 /*
1077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1078 %                                                                             %
1079 %                                                                             %
1080 %                                                                             %
1081 %   R e g i s t e r S I X E L I m a g e                                       %
1082 %                                                                             %
1083 %                                                                             %
1084 %                                                                             %
1085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1086 %
1087 %  RegisterSIXELImage() adds attributes for the SIXEL image format to
1088 %  the list of supported formats.  The attributes include the image format
1089 %  tag, a method to read and/or write the format, whether the format
1090 %  supports the saving of more than one frame to the same file or blob,
1091 %  whether the format supports native in-memory I/O, and a brief
1092 %  description of the format.
1093 %
1094 %  The format of the RegisterSIXELImage method is:
1095 %
1096 %      size_t RegisterSIXELImage(void)
1097 %
1098 */
1099 ModuleExport size_t RegisterSIXELImage(void)
1100 {
1101   MagickInfo
1102     *entry;
1103
1104   entry=SetMagickInfo("SIXEL");
1105   entry->decoder=(DecodeImageHandler *) ReadSIXELImage;
1106   entry->encoder=(EncodeImageHandler *) WriteSIXELImage;
1107   entry->magick=(IsImageFormatHandler *) IsSIXEL;
1108   entry->adjoin=MagickFalse;
1109   entry->description=ConstantString("DEC SIXEL Graphics Format");
1110   entry->module=ConstantString("SIXEL");
1111   (void) RegisterMagickInfo(entry);
1112   entry=SetMagickInfo("SIX");
1113   entry->decoder=(DecodeImageHandler *) ReadSIXELImage;
1114   entry->encoder=(EncodeImageHandler *) WriteSIXELImage;
1115   entry->magick=(IsImageFormatHandler *) IsSIXEL;
1116   entry->adjoin=MagickFalse;
1117   entry->description=ConstantString("DEC SIXEL Graphics Format");
1118   entry->module=ConstantString("SIX");
1119   (void) RegisterMagickInfo(entry);
1120   return(MagickImageCoderSignature);
1121 }
1122 \f
1123 /*
1124 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1125 %                                                                             %
1126 %                                                                             %
1127 %                                                                             %
1128 %   U n r e g i s t e r S I X E L I m a g e                                   %
1129 %                                                                             %
1130 %                                                                             %
1131 %                                                                             %
1132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1133 %
1134 %  UnregisterSIXELImage() removes format registrations made by the
1135 %  SIXEL module from the list of supported formats.
1136 %
1137 %  The format of the UnregisterSIXELImage method is:
1138 %
1139 %      UnregisterSIXELImage(void)
1140 %
1141 */
1142 ModuleExport void UnregisterSIXELImage(void)
1143 {
1144   (void) UnregisterMagickInfo("SIXEL");
1145   (void) UnregisterMagickInfo("SIX");
1146 }
1147 \f
1148 /*
1149 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1150 %                                                                             %
1151 %                                                                             %
1152 %                                                                             %
1153 %   W r i t e S I X E L I m a g e                                             %
1154 %                                                                             %
1155 %                                                                             %
1156 %                                                                             %
1157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1158 %
1159 %  WriteSIXELImage() writes an image to a file in the X pixmap format.
1160 %
1161 %  The format of the WriteSIXELImage method is:
1162 %
1163 %      MagickBooleanType WriteSIXELImage(const ImageInfo *image_info,
1164 %        Image *image,ExceptionInfo *exception)
1165 %
1166 %  A description of each parameter follows.
1167 %
1168 %    o image_info: the image info.
1169 %
1170 %    o image:  The image.
1171 %
1172 %    o exception: return any errors or warnings in this structure.
1173 %
1174 */
1175 static MagickBooleanType WriteSIXELImage(const ImageInfo *image_info,Image *image,
1176   ExceptionInfo *exception)
1177 {
1178   MagickBooleanType
1179     status;
1180
1181   register const Quantum
1182     *q;
1183
1184   register ssize_t
1185     i,
1186     x;
1187
1188   ssize_t
1189     opacity,
1190     y;
1191
1192   sixel_output_t
1193     *output;
1194
1195   unsigned char
1196     sixel_palette[256 * 3],
1197     *sixel_pixels;
1198
1199   /*
1200     Open output image file.
1201   */
1202   assert(image_info != (const ImageInfo *) NULL);
1203   assert(image_info->signature == MagickSignature);
1204   assert(image != (Image *) NULL);
1205   assert(image->signature == MagickSignature);
1206   if (image->debug != MagickFalse)
1207     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1208   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1209   if (status == MagickFalse)
1210     return(status);
1211   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1212     (void) TransformImageColorspace(image,sRGBColorspace,exception);
1213   opacity=(-1);
1214   if (image->alpha_trait != BlendPixelTrait)
1215     {
1216       if ((image->storage_class == DirectClass) || (image->colors > 256))
1217         (void) SetImageType(image,PaletteType,exception);
1218     }
1219   else
1220     {
1221       MagickRealType
1222         alpha,
1223         beta;
1224
1225       /*
1226         Identify transparent colormap index.
1227       */
1228       if ((image->storage_class == DirectClass) || (image->colors > 256))
1229         (void) SetImageType(image,PaletteBilevelMatteType,exception);
1230       for (i=0; i < (ssize_t) image->colors; i++)
1231         if (image->colormap[i].alpha != OpaqueAlpha)
1232           {
1233             if (opacity < 0)
1234               {
1235                 opacity=i;
1236                 continue;
1237               }
1238             alpha=image->colormap[i].alpha;
1239             beta=image->colormap[opacity].alpha;
1240             if (alpha < beta)
1241               opacity=i;
1242           }
1243       if (opacity == -1)
1244         {
1245           (void) SetImageType(image,PaletteBilevelMatteType,exception);
1246           for (i=0; i < (ssize_t) image->colors; i++)
1247             if (image->colormap[i].alpha != OpaqueAlpha)
1248               {
1249                 if (opacity < 0)
1250                   {
1251                     opacity=i;
1252                     continue;
1253                   }
1254                 alpha=image->colormap[i].alpha;
1255                 beta=image->colormap[opacity].alpha;
1256                 if (alpha < beta)
1257                   opacity=i;
1258               }
1259         }
1260       if (opacity >= 0)
1261         {
1262           image->colormap[opacity].red=image->transparent_color.red;
1263           image->colormap[opacity].green=image->transparent_color.green;
1264           image->colormap[opacity].blue=image->transparent_color.blue;
1265         }
1266     }
1267   /*
1268     SIXEL header.
1269   */
1270   for (i=0; i < (ssize_t) image->colors; i++)
1271   {
1272     sixel_palette[i * 3 + 0] = ScaleQuantumToChar(image->colormap[i].red);
1273     sixel_palette[i * 3 + 1] = ScaleQuantumToChar(image->colormap[i].green);
1274     sixel_palette[i * 3 + 2] = ScaleQuantumToChar(image->colormap[i].blue);
1275   }
1276
1277   /*
1278     Define SIXEL pixels.
1279   */
1280   output = sixel_output_create(image);
1281   sixel_pixels =(unsigned char *) AcquireQuantumMemory((size_t) image->columns * image->rows,1);
1282   for (y=0; y < (ssize_t) image->rows; y++)
1283   {
1284     q=GetVirtualPixels(image,0,y,image->columns,1,exception);
1285     for (x=0; x < (ssize_t) image->columns; x++)
1286       {
1287         sixel_pixels[y * image->columns + x] = ((ssize_t) GetPixelIndex(image, q));
1288         q+=GetPixelChannels(image);
1289       }
1290   }
1291   status = sixel_encode_impl(sixel_pixels, image->columns, image->rows,
1292                           sixel_palette, image->colors, -1,
1293                           output);
1294   sixel_pixels =(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1295   output = (sixel_output_t *) RelinquishMagickMemory(output);
1296   (void) CloseBlob(image);
1297   return(status);
1298 }