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