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