Coverage Report

Created: 2025-11-14 07:32

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