master ohmu/fastfetch-openbsd / src / 3rdparty / sixel / tosixel.c
ohmu  ·  2026-10-15
   1/*
   2 * this file is derived from "sixel" original version (2014-3-2)
   3 * http://nanno.dip.jp/softlib/man/rlogin/sixel.tar.gz
   4 *
   5 * Initial developer of this file is kmiya@culti.
   6 *
   7 * He distributes it under very permissive license which permits
   8 * useing, copying, modification, redistribution, and all other
   9 * public activities without any restrictions.
  10 *
  11 * He declares this is compatible with MIT/BSD/GPL.
  12 *
  13 * Hayaki Saito (saitoha@me.com) modified this and re-licensed
  14 * it under the MIT license.
  15 *
  16 * Araki Ken added high-color encoding mode(sixel_encode_highcolor)
  17 * extension.
  18 *
  19 */
  20#include "config.h"
  21
  22/* STDC_HEADERS */
  23#include <stdio.h>
  24#include <stdlib.h>
  25
  26#if HAVE_STRING_H
  27# include <string.h>
  28#endif  /* HAVE_STRING_H */
  29#if HAVE_LIMITS_H
  30# include <limits.h>
  31#endif  /* HAVE_LIMITS_H */
  32#if HAVE_INTTYPES_H
  33# include <inttypes.h>
  34#endif  /* HAVE_INTTYPES_H */
  35
  36#include <sixel.h>
  37#include "output.h"
  38#include "dither.h"
  39
  40#define DCS_START_7BIT       "\033P"
  41#define DCS_START_7BIT_SIZE  (sizeof(DCS_START_7BIT) - 1)
  42#define DCS_START_8BIT       "\220"
  43#define DCS_START_8BIT_SIZE  (sizeof(DCS_START_8BIT) - 1)
  44#define DCS_END_7BIT         "\033\\"
  45#define DCS_END_7BIT_SIZE    (sizeof(DCS_END_7BIT) - 1)
  46#define DCS_END_8BIT         "\234"
  47#define DCS_END_8BIT_SIZE    (sizeof(DCS_END_8BIT) - 1)
  48#define DCS_7BIT(x)          DCS_START_7BIT x DCS_END_7BIT
  49#define DCS_8BIT(x)          DCS_START_8BIT x DCS_END_8BIT
  50#define SCREEN_PACKET_SIZE   256
  51
  52enum {
  53    PALETTE_HIT    = 1,
  54    PALETTE_CHANGE = 2
  55};
  56
  57/* implementation */
  58
  59/* GNU Screen penetration */
  60static void
  61sixel_penetrate(
  62    sixel_output_t  /* in */    *output,        /* output context */
  63    int             /* in */    nwrite,         /* output size */
  64    char const      /* in */    *dcs_start,     /* DCS introducer */
  65    char const      /* in */    *dcs_end,       /* DCS terminator */
  66    int const       /* in */    dcs_start_size, /* size of DCS introducer */
  67    int const       /* in */    dcs_end_size)   /* size of DCS terminator */
  68{
  69    int pos;
  70    int const splitsize = SCREEN_PACKET_SIZE
  71                        - dcs_start_size - dcs_end_size;
  72
  73    for (pos = 0; pos < nwrite; pos += splitsize) {
  74        output->fn_write((char *)dcs_start, dcs_start_size, output->priv);
  75        output->fn_write(((char *)output->buffer) + pos,
  76                          nwrite - pos < splitsize ? nwrite - pos: splitsize,
  77                          output->priv);
  78        output->fn_write((char *)dcs_end, dcs_end_size, output->priv);
  79    }
  80}
  81
  82
  83static void
  84sixel_advance(sixel_output_t *output, int nwrite)
  85{
  86    if ((output->pos += nwrite) >= SIXEL_OUTPUT_PACKET_SIZE) {
  87        if (output->penetrate_multiplexer) {
  88            sixel_penetrate(output,
  89                            SIXEL_OUTPUT_PACKET_SIZE,
  90                            DCS_START_7BIT,
  91                            DCS_END_7BIT,
  92                            DCS_START_7BIT_SIZE,
  93                            DCS_END_7BIT_SIZE);
  94        } else {
  95            output->fn_write((char *)output->buffer,
  96                             SIXEL_OUTPUT_PACKET_SIZE, output->priv);
  97        }
  98        memcpy(output->buffer,
  99               output->buffer + SIXEL_OUTPUT_PACKET_SIZE,
 100               (size_t)(output->pos -= SIXEL_OUTPUT_PACKET_SIZE));
 101    }
 102}
 103
 104
 105static void
 106sixel_putc(unsigned char *buffer, unsigned char value)
 107{
 108    *buffer = value;
 109}
 110
 111
 112static void
 113sixel_puts(unsigned char *buffer, char const *value, int size)
 114{
 115    memcpy(buffer, (void *)value, (size_t)size);
 116}
 117
 118
 119#if HAVE_LDIV
 120static int
 121sixel_putnum_impl(char *buffer, long value, int pos)
 122{
 123    ldiv_t r;
 124
 125    r = ldiv(value, 10);
 126    if (r.quot > 0) {
 127        pos = sixel_putnum_impl(buffer, r.quot, pos);
 128    }
 129    *(buffer + pos) = '0' + r.rem;
 130    return pos + 1;
 131}
 132#endif  /* HAVE_LDIV */
 133
 134
 135static int
 136sixel_putnum(char *buffer, int value)
 137{
 138    int pos;
 139
 140#if HAVE_LDIV
 141    pos = sixel_putnum_impl(buffer, value, 0);
 142#else
 143    pos = sprintf(buffer, "%d", value);
 144#endif  /* HAVE_LDIV */
 145
 146    return pos;
 147}
 148
 149
 150static SIXELSTATUS
 151sixel_put_flash(sixel_output_t *const output)
 152{
 153    int n;
 154    int nwrite;
 155
 156    if (output->has_gri_arg_limit) {  /* VT240 Max 255 ? */
 157        while (output->save_count > 255) {
 158            /* argument of DECGRI('!') is limitted to 255 in real VT */
 159            sixel_puts(output->buffer + output->pos, "!255", 4);
 160            sixel_advance(output, 4);
 161            sixel_putc(output->buffer + output->pos, output->save_pixel);
 162            sixel_advance(output, 1);
 163            output->save_count -= 255;
 164        }
 165    }
 166
 167    if (output->save_count > 3) {
 168        /* DECGRI Graphics Repeat Introducer ! Pn Ch */
 169        sixel_putc(output->buffer + output->pos, '!');
 170        sixel_advance(output, 1);
 171        nwrite = sixel_putnum((char *)output->buffer + output->pos, output->save_count);
 172        sixel_advance(output, nwrite);
 173        sixel_putc(output->buffer + output->pos, output->save_pixel);
 174        sixel_advance(output, 1);
 175    } else {
 176        for (n = 0; n < output->save_count; n++) {
 177            output->buffer[output->pos] = output->save_pixel;
 178            sixel_advance(output, 1);
 179        }
 180    }
 181
 182    output->save_pixel = 0;
 183    output->save_count = 0;
 184
 185    return 0;
 186}
 187
 188
 189static SIXELSTATUS
 190sixel_put_pixel(sixel_output_t *const output, int pix)
 191{
 192    SIXELSTATUS status = SIXEL_FALSE;
 193
 194    if (pix < 0 || pix > '?') {
 195        pix = 0;
 196    }
 197
 198    pix += '?';
 199
 200    if (pix == output->save_pixel) {
 201        output->save_count++;
 202    } else {
 203        status = sixel_put_flash(output);
 204        if (SIXEL_FAILED(status)) {
 205            goto end;
 206        }
 207        output->save_pixel = pix;
 208        output->save_count = 1;
 209    }
 210
 211    status = SIXEL_OK;
 212
 213end:
 214    return status;
 215}
 216
 217static SIXELSTATUS
 218sixel_node_new(sixel_node_t **np, sixel_allocator_t *allocator)
 219{
 220    SIXELSTATUS status = SIXEL_FALSE;
 221
 222    *np = (sixel_node_t *)sixel_allocator_malloc(allocator,
 223                                                 sizeof(sixel_node_t));
 224    if (np == NULL) {
 225        sixel_helper_set_additional_message(
 226            "sixel_node_new: sixel_allocator_malloc() failed.");
 227        status = SIXEL_BAD_ALLOCATION;
 228        goto end;
 229    }
 230
 231    status = SIXEL_OK;
 232
 233end:
 234    return status;
 235}
 236
 237static void
 238sixel_node_del(sixel_output_t *output, sixel_node_t *np)
 239{
 240    sixel_node_t *tp;
 241
 242    if ((tp = output->node_top) == np) {
 243        output->node_top = np->next;
 244    } else {
 245        while (tp->next != NULL) {
 246            if (tp->next == np) {
 247                tp->next = np->next;
 248                break;
 249            }
 250            tp = tp->next;
 251        }
 252    }
 253
 254    np->next = output->node_free;
 255    output->node_free = np;
 256}
 257
 258
 259static SIXELSTATUS
 260sixel_put_node(
 261    sixel_output_t /* in */     *output,  /* output context */
 262    int            /* in/out */ *x,       /* header position */
 263    sixel_node_t   /* in */     *np,      /* node object */
 264    int            /* in */     ncolors,  /* number of palette colors */
 265    int            /* in */     keycolor) /* transparent color number */
 266{
 267    SIXELSTATUS status = SIXEL_FALSE;
 268    int nwrite;
 269
 270    if (ncolors != 2 || keycolor == (-1)) {
 271        /* designate palette index */
 272        if (output->active_palette != np->pal) {
 273            sixel_putc(output->buffer + output->pos, '#');
 274            sixel_advance(output, 1);
 275            nwrite = sixel_putnum((char *)output->buffer + output->pos, np->pal);
 276            sixel_advance(output, nwrite);
 277            output->active_palette = np->pal;
 278        }
 279    }
 280
 281    for (; *x < np->sx; ++*x) {
 282        status = sixel_put_pixel(output, 0);
 283        if (SIXEL_FAILED(status)) {
 284            goto end;
 285        }
 286    }
 287
 288    for (; *x < np->mx; ++*x) {
 289        status = sixel_put_pixel(output, np->map[*x]);
 290        if (SIXEL_FAILED(status)) {
 291            goto end;
 292        }
 293    }
 294
 295    status = sixel_put_flash(output);
 296    if (SIXEL_FAILED(status)) {
 297        goto end;
 298    }
 299
 300end:
 301    return status;
 302}
 303
 304
 305static SIXELSTATUS
 306sixel_encode_header(int width, int height, sixel_output_t *output)
 307{
 308    SIXELSTATUS status = SIXEL_FALSE;
 309    int nwrite;
 310
 311    /* fastfetch: P2 = 1 asks for a transparent background, so a pixel we leave unset keeps
 312     * whatever the terminal already had there. Upstream leaves it at 0, which tells the terminal
 313     * to fill unset pixels with colour-table entry 0 instead -- and that entry is defined by the
 314     * image's own palette, so a keycolor image comes out with a solid rectangle behind it rather
 315     * than transparency. Measured on Windows Terminal, which implements the VT340 semantics;
 316     * ImageMagick's SIXEL coder emits "0;1;0" here and chafa emits the same.
 317     * The trimming below drops the trailing zero, so the envelope is "0;1" -- P3 defaults to 0. */
 318    int p[3] = {0, 1, 0};
 319    int pcount = 3;
 320    int use_raster_attributes = 1;
 321
 322    output->pos = 0;
 323
 324    if (!output->skip_dcs_envelope) {
 325        if (output->has_8bit_control) {
 326            sixel_puts(output->buffer + output->pos,
 327                       DCS_START_8BIT,
 328                       DCS_START_8BIT_SIZE);
 329            sixel_advance(output, DCS_START_8BIT_SIZE);
 330        } else {
 331            sixel_puts(output->buffer + output->pos,
 332                       DCS_START_7BIT,
 333                       DCS_START_7BIT_SIZE);
 334            sixel_advance(output, DCS_START_7BIT_SIZE);
 335        }
 336    }
 337
 338    if (p[2] == 0) {
 339        pcount--;
 340        if (p[1] == 0) {
 341            pcount--;
 342            if (p[0] == 0) {
 343                pcount--;
 344            }
 345        }
 346    }
 347
 348    if (pcount > 0) {
 349        nwrite = sixel_putnum((char *)output->buffer + output->pos, p[0]);
 350        sixel_advance(output, nwrite);
 351        if (pcount > 1) {
 352            sixel_putc(output->buffer + output->pos, ';');
 353            sixel_advance(output, 1);
 354            nwrite = sixel_putnum((char *)output->buffer + output->pos, p[1]);
 355            sixel_advance(output, nwrite);
 356            if (pcount > 2) {
 357                sixel_putc(output->buffer + output->pos, ';');
 358                sixel_advance(output, 1);
 359                nwrite = sixel_putnum((char *)output->buffer + output->pos, p[2]);
 360                sixel_advance(output, nwrite);
 361            }
 362        }
 363    }
 364
 365    sixel_putc(output->buffer + output->pos, 'q');
 366    sixel_advance(output, 1);
 367
 368    if (use_raster_attributes) {
 369        sixel_puts(output->buffer + output->pos, "\"1;1;", 5);
 370        sixel_advance(output, 5);
 371        nwrite = sixel_putnum((char *)output->buffer + output->pos, width);
 372        sixel_advance(output, nwrite);
 373        sixel_putc(output->buffer + output->pos, ';');
 374        sixel_advance(output, 1);
 375        nwrite = sixel_putnum((char *)output->buffer + output->pos, height);
 376        sixel_advance(output, nwrite);
 377    }
 378
 379    status = SIXEL_OK;
 380
 381    return status;
 382}
 383
 384
 385static SIXELSTATUS
 386output_rgb_palette_definition(
 387    sixel_output_t /* in */ *output,
 388    unsigned char  /* in */ *palette,
 389    int            /* in */ n,
 390    int            /* in */ keycolor
 391)
 392{
 393    SIXELSTATUS status = SIXEL_FALSE;
 394    int nwrite;
 395
 396    if (n != keycolor) {
 397        /* DECGCI Graphics Color Introducer  # Pc ; Pu; Px; Py; Pz */
 398        sixel_putc(output->buffer + output->pos, '#');
 399        sixel_advance(output, 1);
 400        nwrite = sixel_putnum((char *)output->buffer + output->pos, n);
 401        sixel_advance(output, nwrite);
 402        sixel_puts(output->buffer + output->pos, ";2;", 3);
 403        sixel_advance(output, 3);
 404        nwrite = sixel_putnum((char *)output->buffer + output->pos,
 405                              (palette[n * 3 + 0] * 100 + 127) / 255);
 406        sixel_advance(output, nwrite);
 407        sixel_putc(output->buffer + output->pos, ';');
 408        sixel_advance(output, 1);
 409        nwrite = sixel_putnum((char *)output->buffer + output->pos,
 410                              (palette[n * 3 + 1] * 100 + 127) / 255);
 411        sixel_advance(output, nwrite);
 412        sixel_putc(output->buffer + output->pos, ';');
 413        sixel_advance(output, 1);
 414        nwrite = sixel_putnum((char *)output->buffer + output->pos,
 415                              (palette[n * 3 + 2] * 100 + 127) / 255);
 416        sixel_advance(output, nwrite);
 417    }
 418
 419    status = SIXEL_OK;
 420
 421    return status;
 422}
 423
 424
 425static SIXELSTATUS
 426output_hls_palette_definition(
 427    sixel_output_t /* in */ *output,
 428    unsigned char  /* in */ *palette,
 429    int            /* in */ n,
 430    int            /* in */ keycolor
 431)
 432{
 433    SIXELSTATUS status = SIXEL_FALSE;
 434    int h;
 435    int l;
 436    int s;
 437    int r;
 438    int g;
 439    int b;
 440    int max;
 441    int min;
 442    int nwrite;
 443
 444    if (n != keycolor) {
 445        r = palette[n * 3 + 0];
 446        g = palette[n * 3 + 1];
 447        b = palette[n * 3 + 2];
 448        max = r > g ? (r > b ? r: b): (g > b ? g: b);
 449        min = r < g ? (r < b ? r: b): (g < b ? g: b);
 450        l = ((max + min) * 100 + 255) / 510;
 451        if (max == min) {
 452            h = s = 0;
 453        } else {
 454            if (l < 50) {
 455                s = ((max - min) * 100) / (max + min);
 456            } else {
 457                s = ((max - min) * 100) / ((255 - max) + (255 - min));
 458            }
 459            if (r == max) {
 460                h = 120 + (g - b) * 60 / (max - min);
 461            } else if (g == max) {
 462                h = 240 + (b - r) * 60 / (max - min);
 463            } else if (r < g) /* if (b == max) */ {
 464                h = 360 + (r - g) * 60 / (max - min);
 465            } else {
 466                h = 0 + (r - g) * 60 / (max - min);
 467            }
 468        }
 469        /* DECGCI Graphics Color Introducer  # Pc ; Pu; Px; Py; Pz */
 470        sixel_putc(output->buffer + output->pos, '#');
 471        sixel_advance(output, 1);
 472        nwrite = sixel_putnum((char *)output->buffer + output->pos, n);
 473        sixel_advance(output, nwrite);
 474        sixel_puts(output->buffer + output->pos, ";1;", 3);
 475        sixel_advance(output, 3);
 476        nwrite = sixel_putnum((char *)output->buffer + output->pos, h);
 477        sixel_advance(output, nwrite);
 478        sixel_putc(output->buffer + output->pos, ';');
 479        sixel_advance(output, 1);
 480        nwrite = sixel_putnum((char *)output->buffer + output->pos, l);
 481        sixel_advance(output, nwrite);
 482        sixel_putc(output->buffer + output->pos, ';');
 483        sixel_advance(output, 1);
 484        nwrite = sixel_putnum((char *)output->buffer + output->pos, s);
 485        sixel_advance(output, nwrite);
 486    }
 487
 488    status = SIXEL_OK;
 489    return status;
 490}
 491
 492
 493static SIXELSTATUS
 494sixel_encode_body(
 495    sixel_index_t       /* in */ *pixels,
 496    int                 /* in */ width,
 497    int                 /* in */ height,
 498    unsigned char       /* in */ *palette,
 499    int                 /* in */ ncolors,
 500    int                 /* in */ keycolor,
 501    int                 /* in */ bodyonly,
 502    sixel_output_t      /* in */ *output,
 503    unsigned char       /* in */ *palstate,
 504    sixel_allocator_t   /* in */ *allocator)
 505{
 506    SIXELSTATUS status = SIXEL_FALSE;
 507    int x;
 508    int y;
 509    int i;
 510    int n;
 511    int c;
 512    int sx;
 513    int mx;
 514    int len;
 515    int pix;
 516    char *map = NULL;
 517    int check_integer_overflow;
 518    sixel_node_t *np, *tp, top;
 519    int fillable;
 520
 521    if (ncolors < 1) {
 522        status = SIXEL_BAD_ARGUMENT;
 523        goto end;
 524    }
 525    len = ncolors * width;
 526    output->active_palette = (-1);
 527
 528    map = (char *)sixel_allocator_calloc(allocator,
 529                                         (size_t)len,
 530                                         sizeof(char));
 531    if (map == NULL) {
 532        sixel_helper_set_additional_message(
 533            "sixel_encode_body: sixel_allocator_calloc() failed.");
 534        status = SIXEL_BAD_ALLOCATION;
 535        goto end;
 536    }
 537
 538    if (!bodyonly && (ncolors != 2 || keycolor == (-1))) {
 539        if (output->palette_type == SIXEL_PALETTETYPE_HLS) {
 540            for (n = 0; n < ncolors; n++) {
 541                status = output_hls_palette_definition(output, palette, n, keycolor);
 542                if (SIXEL_FAILED(status)) {
 543                    goto end;
 544                }
 545            }
 546        } else {
 547            for (n = 0; n < ncolors; n++) {
 548                status = output_rgb_palette_definition(output, palette, n, keycolor);
 549                if (SIXEL_FAILED(status)) {
 550                    goto end;
 551                }
 552            }
 553        }
 554    }
 555
 556    for (y = i = 0; y < height; y++) {
 557        if (output->encode_policy != SIXEL_ENCODEPOLICY_SIZE) {
 558            fillable = 0;
 559        } else if (palstate) {
 560            /* high color sixel */
 561            pix = pixels[(y - i) * width];
 562            if (pix >= ncolors) {
 563                fillable = 0;
 564            } else {
 565                fillable = 1;
 566            }
 567        } else {
 568            /* normal sixel */
 569            fillable = 1;
 570        }
 571        for (x = 0; x < width; x++) {
 572            if (y > INT_MAX / width) {
 573                /* integer overflow */
 574                sixel_helper_set_additional_message(
 575                    "sixel_encode_body: integer overflow detected."
 576                    " (y > INT_MAX)");
 577                status = SIXEL_BAD_INTEGER_OVERFLOW;
 578                goto end;
 579            }
 580            check_integer_overflow = y * width;
 581            if (check_integer_overflow > INT_MAX - x) {
 582                /* integer overflow */
 583                sixel_helper_set_additional_message(
 584                    "sixel_encode_body: integer overflow detected."
 585                    " (y * width > INT_MAX - x)");
 586                status = SIXEL_BAD_INTEGER_OVERFLOW;
 587                goto end;
 588            }
 589            pix = pixels[check_integer_overflow + x];  /* color index */
 590            if (pix >= 0 && pix < ncolors && pix != keycolor) {
 591                if (pix > INT_MAX / width) {
 592                    /* integer overflow */
 593                    sixel_helper_set_additional_message(
 594                        "sixel_encode_body: integer overflow detected."
 595                        " (pix > INT_MAX / width)");
 596                    status = SIXEL_BAD_INTEGER_OVERFLOW;
 597                    goto end;
 598                }
 599                check_integer_overflow = pix * width;
 600                if (check_integer_overflow > INT_MAX - x) {
 601                    /* integer overflow */
 602                    sixel_helper_set_additional_message(
 603                        "sixel_encode_body: integer overflow detected."
 604                        " (pix * width > INT_MAX - x)");
 605                    status = SIXEL_BAD_INTEGER_OVERFLOW;
 606                    goto end;
 607                }
 608                map[pix * width + x] |= (1 << i);
 609            }
 610            else if (!palstate) {
 611                fillable = 0;
 612            }
 613        }
 614
 615        if (++i < 6 && (y + 1) < height) {
 616            continue;
 617        }
 618
 619        for (c = 0; c < ncolors; c++) {
 620            for (sx = 0; sx < width; sx++) {
 621                if (*(map + c * width + sx) == 0) {
 622                    continue;
 623                }
 624
 625                for (mx = sx + 1; mx < width; mx++) {
 626                    if (*(map + c * width + mx) != 0) {
 627                        continue;
 628                    }
 629
 630                    for (n = 1; (mx + n) < width; n++) {
 631                        if (*(map + c * width + mx + n) != 0) {
 632                            break;
 633                        }
 634                    }
 635
 636                    if (n >= 10 || (mx + n) >= width) {
 637                        break;
 638                    }
 639                    mx = mx + n - 1;
 640                }
 641
 642                if ((np = output->node_free) != NULL) {
 643                    output->node_free = np->next;
 644                } else {
 645                    status = sixel_node_new(&np, allocator);
 646                    if (SIXEL_FAILED(status)) {
 647                        goto end;
 648                    }
 649                }
 650
 651                np->pal = c;
 652                np->sx = sx;
 653                np->mx = mx;
 654                np->map = map + c * width;
 655
 656                top.next = output->node_top;
 657                tp = &top;
 658
 659                while (tp->next != NULL) {
 660                    if (np->sx < tp->next->sx) {
 661                        break;
 662                    } else if (np->sx == tp->next->sx && np->mx > tp->next->mx) {
 663                        break;
 664                    }
 665                    tp = tp->next;
 666                }
 667
 668                np->next = tp->next;
 669                tp->next = np;
 670                output->node_top = top.next;
 671
 672                sx = mx - 1;
 673            }
 674
 675        }
 676
 677        if (y != 5) {
 678            /* DECGNL Graphics Next Line */
 679            output->buffer[output->pos] = '-';
 680            sixel_advance(output, 1);
 681        }
 682
 683        for (x = 0; (np = output->node_top) != NULL;) {
 684            sixel_node_t *next;
 685            if (x > np->sx) {
 686                /* DECGCR Graphics Carriage Return */
 687                output->buffer[output->pos] = '$';
 688                sixel_advance(output, 1);
 689                x = 0;
 690            }
 691
 692            if (fillable) {
 693                memset(np->map + np->sx, (1 << i) - 1, (size_t)(np->mx - np->sx));
 694            }
 695            status = sixel_put_node(output, &x, np, ncolors, keycolor);
 696            if (SIXEL_FAILED(status)) {
 697                goto end;
 698            }
 699            next = np->next;
 700            sixel_node_del(output, np);
 701            np = next;
 702
 703            while (np != NULL) {
 704                if (np->sx < x) {
 705                    np = np->next;
 706                    continue;
 707                }
 708
 709                if (fillable) {
 710                    memset(np->map + np->sx, (1 << i) - 1, (size_t)(np->mx - np->sx));
 711                }
 712                status = sixel_put_node(output, &x, np, ncolors, keycolor);
 713                if (SIXEL_FAILED(status)) {
 714                    goto end;
 715                }
 716                next = np->next;
 717                sixel_node_del(output, np);
 718                np = next;
 719            }
 720
 721            fillable = 0;
 722        }
 723
 724        i = 0;
 725        memset(map, 0, (size_t)len);
 726    }
 727
 728    if (palstate) {
 729        output->buffer[output->pos] = '$';
 730        sixel_advance(output, 1);
 731    }
 732
 733    status = SIXEL_OK;
 734
 735end:
 736    /* free nodes */
 737    while ((np = output->node_free) != NULL) {
 738        output->node_free = np->next;
 739        sixel_allocator_free(allocator, np);
 740    }
 741    output->node_top = NULL;
 742
 743    sixel_allocator_free(allocator, map);
 744
 745    return status;
 746}
 747
 748
 749static SIXELSTATUS
 750sixel_encode_footer(sixel_output_t *output)
 751{
 752    SIXELSTATUS status = SIXEL_FALSE;
 753
 754    if (!output->skip_dcs_envelope && !output->penetrate_multiplexer) {
 755        if (output->has_8bit_control) {
 756            sixel_puts(output->buffer + output->pos,
 757                       DCS_END_8BIT, DCS_END_8BIT_SIZE);
 758            sixel_advance(output, DCS_END_8BIT_SIZE);
 759        } else {
 760            sixel_puts(output->buffer + output->pos,
 761                       DCS_END_7BIT, DCS_END_7BIT_SIZE);
 762            sixel_advance(output, DCS_END_7BIT_SIZE);
 763        }
 764    }
 765
 766    /* flush buffer */
 767    if (output->pos > 0) {
 768        if (output->penetrate_multiplexer) {
 769            sixel_penetrate(output, output->pos,
 770                            DCS_START_7BIT,
 771                            DCS_END_7BIT,
 772                            DCS_START_7BIT_SIZE,
 773                            DCS_END_7BIT_SIZE);
 774            output->fn_write((char *)DCS_7BIT("\033") DCS_7BIT("\\"),
 775                             (DCS_START_7BIT_SIZE + 1 + DCS_END_7BIT_SIZE) * 2,
 776                             output->priv);
 777        } else {
 778            output->fn_write((char *)output->buffer, output->pos, output->priv);
 779        }
 780    }
 781
 782    status = SIXEL_OK;
 783
 784    return status;
 785}
 786
 787
 788static SIXELSTATUS
 789sixel_encode_dither(
 790    unsigned char   /* in */ *pixels,   /* pixel bytes to be encoded */
 791    int             /* in */ width,     /* width of source image */
 792    int             /* in */ height,    /* height of source image */
 793    sixel_dither_t  /* in */ *dither,   /* dither context */
 794    sixel_output_t  /* in */ *output)   /* output context */
 795{
 796    SIXELSTATUS status = SIXEL_FALSE;
 797    sixel_index_t *paletted_pixels = NULL;
 798    sixel_index_t *input_pixels;
 799    size_t bufsize;
 800
 801    switch (dither->pixelformat) {
 802    case SIXEL_PIXELFORMAT_PAL1:
 803    case SIXEL_PIXELFORMAT_PAL2:
 804    case SIXEL_PIXELFORMAT_PAL4:
 805    case SIXEL_PIXELFORMAT_G1:
 806    case SIXEL_PIXELFORMAT_G2:
 807    case SIXEL_PIXELFORMAT_G4:
 808        bufsize = (sizeof(sixel_index_t) * (size_t)width * (size_t)height * 3UL);
 809        paletted_pixels = (sixel_index_t *)sixel_allocator_malloc(dither->allocator, bufsize);
 810        if (paletted_pixels == NULL) {
 811            sixel_helper_set_additional_message(
 812                "sixel_encode_dither: sixel_allocator_malloc() failed.");
 813            status = SIXEL_BAD_ALLOCATION;
 814            goto end;
 815        }
 816        status = sixel_helper_normalize_pixelformat(paletted_pixels,
 817                                                    &dither->pixelformat,
 818                                                    pixels,
 819                                                    dither->pixelformat,
 820                                                    width, height);
 821        if (SIXEL_FAILED(status)) {
 822            goto end;
 823        }
 824        input_pixels = paletted_pixels;
 825        break;
 826    case SIXEL_PIXELFORMAT_PAL8:
 827    case SIXEL_PIXELFORMAT_G8:
 828    case SIXEL_PIXELFORMAT_GA88:
 829    case SIXEL_PIXELFORMAT_AG88:
 830        input_pixels = pixels;
 831        break;
 832    default:
 833        /* apply palette */
 834        paletted_pixels = sixel_dither_apply_palette(dither, pixels,
 835                                                     width, height);
 836        if (paletted_pixels == NULL) {
 837            status = SIXEL_RUNTIME_ERROR;
 838            goto end;
 839        }
 840        input_pixels = paletted_pixels;
 841        break;
 842    }
 843
 844    status = sixel_encode_header(width, height, output);
 845    if (SIXEL_FAILED(status)) {
 846        goto end;
 847    }
 848
 849    status = sixel_encode_body(input_pixels,
 850                               width,
 851                               height,
 852                               dither->palette,
 853                               dither->ncolors,
 854                               dither->keycolor,
 855                               dither->bodyonly,
 856                               output,
 857                               NULL,
 858                               dither->allocator);
 859    if (SIXEL_FAILED(status)) {
 860        goto end;
 861    }
 862
 863    status = sixel_encode_footer(output);
 864    if (SIXEL_FAILED(status)) {
 865        goto end;
 866    }
 867
 868end:
 869    sixel_allocator_free(dither->allocator, paletted_pixels);
 870
 871    return status;
 872}
 873
 874static void
 875dither_func_none(unsigned char *data, int width)
 876{
 877    (void) data;  /* unused */
 878    (void) width; /* unused */
 879}
 880
 881
 882static void
 883dither_func_fs(unsigned char *data, int width)
 884{
 885    int r, g, b;
 886    int error_r = data[0] & 0x7;
 887    int error_g = data[1] & 0x7;
 888    int error_b = data[2] & 0x7;
 889
 890    /* Floyd Steinberg Method
 891     *          curr    7/16
 892     *  3/16    5/48    1/16
 893     */
 894    r = (data[3 + 0] + (error_r * 5 >> 4));
 895    g = (data[3 + 1] + (error_g * 5 >> 4));
 896    b = (data[3 + 2] + (error_b * 5 >> 4));
 897    data[3 + 0] = r > 0xff ? 0xff: r;
 898    data[3 + 1] = g > 0xff ? 0xff: g;
 899    data[3 + 2] = b > 0xff ? 0xff: b;
 900    r = data[width * 3 - 3 + 0] + (error_r * 3 >> 4);
 901    g = data[width * 3 - 3 + 1] + (error_g * 3 >> 4);
 902    b = data[width * 3 - 3 + 2] + (error_b * 3 >> 4);
 903    data[width * 3 - 3 + 0] = r > 0xff ? 0xff: r;
 904    data[width * 3 - 3 + 1] = g > 0xff ? 0xff: g;
 905    data[width * 3 - 3 + 2] = b > 0xff ? 0xff: b;
 906    r = data[width * 3 + 0] + (error_r * 5 >> 4);
 907    g = data[width * 3 + 1] + (error_g * 5 >> 4);
 908    b = data[width * 3 + 2] + (error_b * 5 >> 4);
 909    data[width * 3 + 0] = r > 0xff ? 0xff: r;
 910    data[width * 3 + 1] = g > 0xff ? 0xff: g;
 911    data[width * 3 + 2] = b > 0xff ? 0xff: b;
 912}
 913
 914
 915static void
 916dither_func_atkinson(unsigned char *data, int width)
 917{
 918    int r, g, b;
 919    int error_r = data[0] & 0x7;
 920    int error_g = data[1] & 0x7;
 921    int error_b = data[2] & 0x7;
 922
 923    error_r += 4;
 924    error_g += 4;
 925    error_b += 4;
 926
 927    /* Atkinson's Method
 928     *          curr    1/8    1/8
 929     *   1/8     1/8    1/8
 930     *           1/8
 931     */
 932    r = data[(width * 0 + 1) * 3 + 0] + (error_r >> 3);
 933    g = data[(width * 0 + 1) * 3 + 1] + (error_g >> 3);
 934    b = data[(width * 0 + 1) * 3 + 2] + (error_b >> 3);
 935    data[(width * 0 + 1) * 3 + 0] = r > 0xff ? 0xff: r;
 936    data[(width * 0 + 1) * 3 + 1] = g > 0xff ? 0xff: g;
 937    data[(width * 0 + 1) * 3 + 2] = b > 0xff ? 0xff: b;
 938    r = data[(width * 0 + 2) * 3 + 0] + (error_r >> 3);
 939    g = data[(width * 0 + 2) * 3 + 1] + (error_g >> 3);
 940    b = data[(width * 0 + 2) * 3 + 2] + (error_b >> 3);
 941    data[(width * 0 + 2) * 3 + 0] = r > 0xff ? 0xff: r;
 942    data[(width * 0 + 2) * 3 + 1] = g > 0xff ? 0xff: g;
 943    data[(width * 0 + 2) * 3 + 2] = b > 0xff ? 0xff: b;
 944    r = data[(width * 1 - 1) * 3 + 0] + (error_r >> 3);
 945    g = data[(width * 1 - 1) * 3 + 1] + (error_g >> 3);
 946    b = data[(width * 1 - 1) * 3 + 2] + (error_b >> 3);
 947    data[(width * 1 - 1) * 3 + 0] = r > 0xff ? 0xff: r;
 948    data[(width * 1 - 1) * 3 + 1] = g > 0xff ? 0xff: g;
 949    data[(width * 1 - 1) * 3 + 2] = b > 0xff ? 0xff: b;
 950    r = data[(width * 1 + 0) * 3 + 0] + (error_r >> 3);
 951    g = data[(width * 1 + 0) * 3 + 1] + (error_g >> 3);
 952    b = data[(width * 1 + 0) * 3 + 2] + (error_b >> 3);
 953    data[(width * 1 + 0) * 3 + 0] = r > 0xff ? 0xff: r;
 954    data[(width * 1 + 0) * 3 + 1] = g > 0xff ? 0xff: g;
 955    data[(width * 1 + 0) * 3 + 2] = b > 0xff ? 0xff: b;
 956    r = (data[(width * 1 + 1) * 3 + 0] + (error_r >> 3));
 957    g = (data[(width * 1 + 1) * 3 + 1] + (error_g >> 3));
 958    b = (data[(width * 1 + 1) * 3 + 2] + (error_b >> 3));
 959    data[(width * 1 + 1) * 3 + 0] = r > 0xff ? 0xff: r;
 960    data[(width * 1 + 1) * 3 + 1] = g > 0xff ? 0xff: g;
 961    data[(width * 1 + 1) * 3 + 2] = b > 0xff ? 0xff: b;
 962    r = (data[(width * 2 + 0) * 3 + 0] + (error_r >> 3));
 963    g = (data[(width * 2 + 0) * 3 + 1] + (error_g >> 3));
 964    b = (data[(width * 2 + 0) * 3 + 2] + (error_b >> 3));
 965    data[(width * 2 + 0) * 3 + 0] = r > 0xff ? 0xff: r;
 966    data[(width * 2 + 0) * 3 + 1] = g > 0xff ? 0xff: g;
 967    data[(width * 2 + 0) * 3 + 2] = b > 0xff ? 0xff: b;
 968}
 969
 970
 971static void
 972dither_func_jajuni(unsigned char *data, int width)
 973{
 974    int r, g, b;
 975    int error_r = data[0] & 0x7;
 976    int error_g = data[1] & 0x7;
 977    int error_b = data[2] & 0x7;
 978
 979    error_r += 4;
 980    error_g += 4;
 981    error_b += 4;
 982
 983    /* Jarvis, Judice & Ninke Method
 984     *                  curr    7/48    5/48
 985     *  3/48    5/48    7/48    5/48    3/48
 986     *  1/48    3/48    5/48    3/48    1/48
 987     */
 988    r = data[(width * 0 + 1) * 3 + 0] + (error_r * 7 / 48);
 989    g = data[(width * 0 + 1) * 3 + 1] + (error_g * 7 / 48);
 990    b = data[(width * 0 + 1) * 3 + 2] + (error_b * 7 / 48);
 991    data[(width * 0 + 1) * 3 + 0] = r > 0xff ? 0xff: r;
 992    data[(width * 0 + 1) * 3 + 1] = g > 0xff ? 0xff: g;
 993    data[(width * 0 + 1) * 3 + 2] = b > 0xff ? 0xff: b;
 994    r = data[(width * 0 + 2) * 3 + 0] + (error_r * 5 / 48);
 995    g = data[(width * 0 + 2) * 3 + 1] + (error_g * 5 / 48);
 996    b = data[(width * 0 + 2) * 3 + 2] + (error_b * 5 / 48);
 997    data[(width * 0 + 2) * 3 + 0] = r > 0xff ? 0xff: r;
 998    data[(width * 0 + 2) * 3 + 1] = g > 0xff ? 0xff: g;
 999    data[(width * 0 + 2) * 3 + 2] = b > 0xff ? 0xff: b;
1000    r = data[(width * 1 - 2) * 3 + 0] + (error_r * 3 / 48);
1001    g = data[(width * 1 - 2) * 3 + 1] + (error_g * 3 / 48);
1002    b = data[(width * 1 - 2) * 3 + 2] + (error_b * 3 / 48);
1003    data[(width * 1 - 2) * 3 + 0] = r > 0xff ? 0xff: r;
1004    data[(width * 1 - 2) * 3 + 1] = g > 0xff ? 0xff: g;
1005    data[(width * 1 - 2) * 3 + 2] = b > 0xff ? 0xff: b;
1006    r = data[(width * 1 - 1) * 3 + 0] + (error_r * 5 / 48);
1007    g = data[(width * 1 - 1) * 3 + 1] + (error_g * 5 / 48);
1008    b = data[(width * 1 - 1) * 3 + 2] + (error_b * 5 / 48);
1009    data[(width * 1 - 1) * 3 + 0] = r > 0xff ? 0xff: r;
1010    data[(width * 1 - 1) * 3 + 1] = g > 0xff ? 0xff: g;
1011    data[(width * 1 - 1) * 3 + 2] = b > 0xff ? 0xff: b;
1012    r = data[(width * 1 + 0) * 3 + 0] + (error_r * 7 / 48);
1013    g = data[(width * 1 + 0) * 3 + 1] + (error_g * 7 / 48);
1014    b = data[(width * 1 + 0) * 3 + 2] + (error_b * 7 / 48);
1015    data[(width * 1 + 0) * 3 + 0] = r > 0xff ? 0xff: r;
1016    data[(width * 1 + 0) * 3 + 1] = g > 0xff ? 0xff: g;
1017    data[(width * 1 + 0) * 3 + 2] = b > 0xff ? 0xff: b;
1018    r = data[(width * 1 + 1) * 3 + 0] + (error_r * 5 / 48);
1019    g = data[(width * 1 + 1) * 3 + 1] + (error_g * 5 / 48);
1020    b = data[(width * 1 + 1) * 3 + 2] + (error_b * 5 / 48);
1021    data[(width * 1 + 1) * 3 + 0] = r > 0xff ? 0xff: r;
1022    data[(width * 1 + 1) * 3 + 1] = g > 0xff ? 0xff: g;
1023    data[(width * 1 + 1) * 3 + 2] = b > 0xff ? 0xff: b;
1024    r = data[(width * 1 + 2) * 3 + 0] + (error_r * 3 / 48);
1025    g = data[(width * 1 + 2) * 3 + 1] + (error_g * 3 / 48);
1026    b = data[(width * 1 + 2) * 3 + 2] + (error_b * 3 / 48);
1027    data[(width * 1 + 2) * 3 + 0] = r > 0xff ? 0xff: r;
1028    data[(width * 1 + 2) * 3 + 1] = g > 0xff ? 0xff: g;
1029    data[(width * 1 + 2) * 3 + 2] = b > 0xff ? 0xff: b;
1030    r = data[(width * 2 - 2) * 3 + 0] + (error_r * 1 / 48);
1031    g = data[(width * 2 - 2) * 3 + 1] + (error_g * 1 / 48);
1032    b = data[(width * 2 - 2) * 3 + 2] + (error_b * 1 / 48);
1033    data[(width * 2 - 2) * 3 + 0] = r > 0xff ? 0xff: r;
1034    data[(width * 2 - 2) * 3 + 1] = g > 0xff ? 0xff: g;
1035    data[(width * 2 - 2) * 3 + 2] = b > 0xff ? 0xff: b;
1036    r = data[(width * 2 - 1) * 3 + 0] + (error_r * 3 / 48);
1037    g = data[(width * 2 - 1) * 3 + 1] + (error_g * 3 / 48);
1038    b = data[(width * 2 - 1) * 3 + 2] + (error_b * 3 / 48);
1039    data[(width * 2 - 1) * 3 + 0] = r > 0xff ? 0xff: r;
1040    data[(width * 2 - 1) * 3 + 1] = g > 0xff ? 0xff: g;
1041    data[(width * 2 - 1) * 3 + 2] = b > 0xff ? 0xff: b;
1042    r = data[(width * 2 + 0) * 3 + 0] + (error_r * 5 / 48);
1043    g = data[(width * 2 + 0) * 3 + 1] + (error_g * 5 / 48);
1044    b = data[(width * 2 + 0) * 3 + 2] + (error_b * 5 / 48);
1045    data[(width * 2 + 0) * 3 + 0] = r > 0xff ? 0xff: r;
1046    data[(width * 2 + 0) * 3 + 1] = g > 0xff ? 0xff: g;
1047    data[(width * 2 + 0) * 3 + 2] = b > 0xff ? 0xff: b;
1048    r = data[(width * 2 + 1) * 3 + 0] + (error_r * 3 / 48);
1049    g = data[(width * 2 + 1) * 3 + 1] + (error_g * 3 / 48);
1050    b = data[(width * 2 + 1) * 3 + 2] + (error_b * 3 / 48);
1051    data[(width * 2 + 1) * 3 + 0] = r > 0xff ? 0xff: r;
1052    data[(width * 2 + 1) * 3 + 1] = g > 0xff ? 0xff: g;
1053    data[(width * 2 + 1) * 3 + 2] = b > 0xff ? 0xff: b;
1054    r = data[(width * 2 + 2) * 3 + 0] + (error_r * 1 / 48);
1055    g = data[(width * 2 + 2) * 3 + 1] + (error_g * 1 / 48);
1056    b = data[(width * 2 + 2) * 3 + 2] + (error_b * 1 / 48);
1057    data[(width * 2 + 2) * 3 + 0] = r > 0xff ? 0xff: r;
1058    data[(width * 2 + 2) * 3 + 1] = g > 0xff ? 0xff: g;
1059    data[(width * 2 + 2) * 3 + 2] = b > 0xff ? 0xff: b;
1060}
1061
1062
1063static void
1064dither_func_stucki(unsigned char *data, int width)
1065{
1066    int r, g, b;
1067    int error_r = data[0] & 0x7;
1068    int error_g = data[1] & 0x7;
1069    int error_b = data[2] & 0x7;
1070
1071    error_r += 4;
1072    error_g += 4;
1073    error_b += 4;
1074
1075    /* Stucki's Method
1076     *                  curr    8/48    4/48
1077     *  2/48    4/48    8/48    4/48    2/48
1078     *  1/48    2/48    4/48    2/48    1/48
1079     */
1080    r = data[(width * 0 + 1) * 3 + 0] + (error_r * 8 / 48);
1081    g = data[(width * 0 + 1) * 3 + 1] + (error_g * 8 / 48);
1082    b = data[(width * 0 + 1) * 3 + 2] + (error_b * 8 / 48);
1083    data[(width * 0 + 1) * 3 + 0] = r > 0xff ? 0xff: r;
1084    data[(width * 0 + 1) * 3 + 1] = g > 0xff ? 0xff: g;
1085    data[(width * 0 + 1) * 3 + 2] = b > 0xff ? 0xff: b;
1086    r = data[(width * 0 + 2) * 3 + 0] + (error_r * 4 / 48);
1087    g = data[(width * 0 + 2) * 3 + 1] + (error_g * 4 / 48);
1088    b = data[(width * 0 + 2) * 3 + 2] + (error_b * 4 / 48);
1089    data[(width * 0 + 2) * 3 + 0] = r > 0xff ? 0xff: r;
1090    data[(width * 0 + 2) * 3 + 1] = g > 0xff ? 0xff: g;
1091    data[(width * 0 + 2) * 3 + 2] = b > 0xff ? 0xff: b;
1092    r = data[(width * 1 - 2) * 3 + 0] + (error_r * 2 / 48);
1093    g = data[(width * 1 - 2) * 3 + 1] + (error_g * 2 / 48);
1094    b = data[(width * 1 - 2) * 3 + 2] + (error_b * 2 / 48);
1095    data[(width * 1 - 2) * 3 + 0] = r > 0xff ? 0xff: r;
1096    data[(width * 1 - 2) * 3 + 1] = g > 0xff ? 0xff: g;
1097    data[(width * 1 - 2) * 3 + 2] = b > 0xff ? 0xff: b;
1098    r = data[(width * 1 - 1) * 3 + 0] + (error_r * 4 / 48);
1099    g = data[(width * 1 - 1) * 3 + 1] + (error_g * 4 / 48);
1100    b = data[(width * 1 - 1) * 3 + 2] + (error_b * 4 / 48);
1101    data[(width * 1 - 1) * 3 + 0] = r > 0xff ? 0xff: r;
1102    data[(width * 1 - 1) * 3 + 1] = g > 0xff ? 0xff: g;
1103    data[(width * 1 - 1) * 3 + 2] = b > 0xff ? 0xff: b;
1104    r = data[(width * 1 + 0) * 3 + 0] + (error_r * 8 / 48);
1105    g = data[(width * 1 + 0) * 3 + 1] + (error_g * 8 / 48);
1106    b = data[(width * 1 + 0) * 3 + 2] + (error_b * 8 / 48);
1107    data[(width * 1 + 0) * 3 + 0] = r > 0xff ? 0xff: r;
1108    data[(width * 1 + 0) * 3 + 1] = g > 0xff ? 0xff: g;
1109    data[(width * 1 + 0) * 3 + 2] = b > 0xff ? 0xff: b;
1110    r = data[(width * 1 + 1) * 3 + 0] + (error_r * 4 / 48);
1111    g = data[(width * 1 + 1) * 3 + 1] + (error_g * 4 / 48);
1112    b = data[(width * 1 + 1) * 3 + 2] + (error_b * 4 / 48);
1113    data[(width * 1 + 1) * 3 + 0] = r > 0xff ? 0xff: r;
1114    data[(width * 1 + 1) * 3 + 1] = g > 0xff ? 0xff: g;
1115    data[(width * 1 + 1) * 3 + 2] = b > 0xff ? 0xff: b;
1116    r = data[(width * 1 + 2) * 3 + 0] + (error_r * 2 / 48);
1117    g = data[(width * 1 + 2) * 3 + 1] + (error_g * 2 / 48);
1118    b = data[(width * 1 + 2) * 3 + 2] + (error_b * 2 / 48);
1119    data[(width * 1 + 2) * 3 + 0] = r > 0xff ? 0xff: r;
1120    data[(width * 1 + 2) * 3 + 1] = g > 0xff ? 0xff: g;
1121    data[(width * 1 + 2) * 3 + 2] = b > 0xff ? 0xff: b;
1122    r = data[(width * 2 - 2) * 3 + 0] + (error_r * 1 / 48);
1123    g = data[(width * 2 - 2) * 3 + 1] + (error_g * 1 / 48);
1124    b = data[(width * 2 - 2) * 3 + 2] + (error_b * 1 / 48);
1125    data[(width * 2 - 2) * 3 + 0] = r > 0xff ? 0xff: r;
1126    data[(width * 2 - 2) * 3 + 1] = g > 0xff ? 0xff: g;
1127    data[(width * 2 - 2) * 3 + 2] = b > 0xff ? 0xff: b;
1128    r = data[(width * 2 - 1) * 3 + 0] + (error_r * 2 / 48);
1129    g = data[(width * 2 - 1) * 3 + 1] + (error_g * 2 / 48);
1130    b = data[(width * 2 - 1) * 3 + 2] + (error_b * 2 / 48);
1131    data[(width * 2 - 1) * 3 + 0] = r > 0xff ? 0xff: r;
1132    data[(width * 2 - 1) * 3 + 1] = g > 0xff ? 0xff: g;
1133    data[(width * 2 - 1) * 3 + 2] = b > 0xff ? 0xff: b;
1134    r = data[(width * 2 + 0) * 3 + 0] + (error_r * 4 / 48);
1135    g = data[(width * 2 + 0) * 3 + 1] + (error_g * 4 / 48);
1136    b = data[(width * 2 + 0) * 3 + 2] + (error_b * 4 / 48);
1137    data[(width * 2 + 0) * 3 + 0] = r > 0xff ? 0xff: r;
1138    data[(width * 2 + 0) * 3 + 1] = g > 0xff ? 0xff: g;
1139    data[(width * 2 + 0) * 3 + 2] = b > 0xff ? 0xff: b;
1140    r = data[(width * 2 + 1) * 3 + 0] + (error_r * 2 / 48);
1141    g = data[(width * 2 + 1) * 3 + 1] + (error_g * 2 / 48);
1142    b = data[(width * 2 + 1) * 3 + 2] + (error_b * 2 / 48);
1143    data[(width * 2 + 1) * 3 + 0] = r > 0xff ? 0xff: r;
1144    data[(width * 2 + 1) * 3 + 1] = g > 0xff ? 0xff: g;
1145    data[(width * 2 + 1) * 3 + 2] = b > 0xff ? 0xff: b;
1146    r = data[(width * 2 + 2) * 3 + 0] + (error_r * 1 / 48);
1147    g = data[(width * 2 + 2) * 3 + 1] + (error_g * 1 / 48);
1148    b = data[(width * 2 + 2) * 3 + 2] + (error_b * 1 / 48);
1149    data[(width * 2 + 2) * 3 + 0] = r > 0xff ? 0xff: r;
1150    data[(width * 2 + 2) * 3 + 1] = g > 0xff ? 0xff: g;
1151    data[(width * 2 + 2) * 3 + 2] = b > 0xff ? 0xff: b;
1152}
1153
1154
1155static void
1156dither_func_burkes(unsigned char *data, int width)
1157{
1158    int r, g, b;
1159    int error_r = data[0] & 0x7;
1160    int error_g = data[1] & 0x7;
1161    int error_b = data[2] & 0x7;
1162
1163    error_r += 2;
1164    error_g += 2;
1165    error_b += 2;
1166
1167    /* Burkes' Method
1168     *                  curr    4/16    2/16
1169     *  1/16    2/16    4/16    2/16    1/16
1170     */
1171    r = data[(width * 0 + 1) * 3 + 0] + (error_r * 4 / 16);
1172    g = data[(width * 0 + 1) * 3 + 1] + (error_g * 4 / 16);
1173    b = data[(width * 0 + 1) * 3 + 2] + (error_b * 4 / 16);
1174    data[(width * 0 + 1) * 3 + 0] = r > 0xff ? 0xff: r;
1175    data[(width * 0 + 1) * 3 + 1] = g > 0xff ? 0xff: g;
1176    data[(width * 0 + 1) * 3 + 2] = b > 0xff ? 0xff: b;
1177    r = data[(width * 0 + 2) * 3 + 0] + (error_r * 2 / 16);
1178    g = data[(width * 0 + 2) * 3 + 1] + (error_g * 2 / 16);
1179    b = data[(width * 0 + 2) * 3 + 2] + (error_b * 2 / 16);
1180    data[(width * 0 + 2) * 3 + 0] = r > 0xff ? 0xff: r;
1181    data[(width * 0 + 2) * 3 + 1] = g > 0xff ? 0xff: g;
1182    data[(width * 0 + 2) * 3 + 2] = b > 0xff ? 0xff: b;
1183    r = data[(width * 1 - 2) * 3 + 0] + (error_r * 1 / 16);
1184    g = data[(width * 1 - 2) * 3 + 1] + (error_g * 1 / 16);
1185    b = data[(width * 1 - 2) * 3 + 2] + (error_b * 1 / 16);
1186    data[(width * 1 - 2) * 3 + 0] = r > 0xff ? 0xff: r;
1187    data[(width * 1 - 2) * 3 + 1] = g > 0xff ? 0xff: g;
1188    data[(width * 1 - 2) * 3 + 2] = b > 0xff ? 0xff: b;
1189    r = data[(width * 1 - 1) * 3 + 0] + (error_r * 2 / 16);
1190    g = data[(width * 1 - 1) * 3 + 1] + (error_g * 2 / 16);
1191    b = data[(width * 1 - 1) * 3 + 2] + (error_b * 2 / 16);
1192    data[(width * 1 - 1) * 3 + 0] = r > 0xff ? 0xff: r;
1193    data[(width * 1 - 1) * 3 + 1] = g > 0xff ? 0xff: g;
1194    data[(width * 1 - 1) * 3 + 2] = b > 0xff ? 0xff: b;
1195    r = data[(width * 1 + 0) * 3 + 0] + (error_r * 4 / 16);
1196    g = data[(width * 1 + 0) * 3 + 1] + (error_g * 4 / 16);
1197    b = data[(width * 1 + 0) * 3 + 2] + (error_b * 4 / 16);
1198    data[(width * 1 + 0) * 3 + 0] = r > 0xff ? 0xff: r;
1199    data[(width * 1 + 0) * 3 + 1] = g > 0xff ? 0xff: g;
1200    data[(width * 1 + 0) * 3 + 2] = b > 0xff ? 0xff: b;
1201    r = data[(width * 1 + 1) * 3 + 0] + (error_r * 2 / 16);
1202    g = data[(width * 1 + 1) * 3 + 1] + (error_g * 2 / 16);
1203    b = data[(width * 1 + 1) * 3 + 2] + (error_b * 2 / 16);
1204    data[(width * 1 + 1) * 3 + 0] = r > 0xff ? 0xff: r;
1205    data[(width * 1 + 1) * 3 + 1] = g > 0xff ? 0xff: g;
1206    data[(width * 1 + 1) * 3 + 2] = b > 0xff ? 0xff: b;
1207    r = data[(width * 1 + 2) * 3 + 0] + (error_r * 1 / 16);
1208    g = data[(width * 1 + 2) * 3 + 1] + (error_g * 1 / 16);
1209    b = data[(width * 1 + 2) * 3 + 2] + (error_b * 1 / 16);
1210    data[(width * 1 + 2) * 3 + 0] = r > 0xff ? 0xff: r;
1211    data[(width * 1 + 2) * 3 + 1] = g > 0xff ? 0xff: g;
1212    data[(width * 1 + 2) * 3 + 2] = b > 0xff ? 0xff: b;
1213}
1214
1215
1216static void
1217dither_func_a_dither(unsigned char *data, int width, int x, int y)
1218{
1219    int c;
1220    float value;
1221    float mask;
1222
1223    (void) width; /* unused */
1224
1225    for (c = 0; c < 3; c ++) {
1226        mask = (((x + c * 17) + y * 236) * 119) & 255;
1227        mask = ((mask - 128) / 256.0f) ;
1228        value = data[c] + mask;
1229        if (value < 0) {
1230            value = 0;
1231        }
1232        value = value > 255 ? 255 : value;
1233        data[c] = value;
1234    }
1235}
1236
1237
1238static void
1239dither_func_x_dither(unsigned char *data, int width, int x, int y)
1240{
1241    int c;
1242    float value;
1243    float mask;
1244
1245    (void) width;  /* unused */
1246
1247    for (c = 0; c < 3; c ++) {
1248        mask = (((x + c * 17) ^ y * 236) * 1234) & 511;
1249        mask = ((mask - 128) / 512.0f) ;
1250        value = data[c] + mask;
1251        if (value < 0) {
1252            value = 0;
1253        }
1254        value = value > 255 ? 255 : value;
1255        data[c] = value;
1256    }
1257}
1258
1259
1260static void
1261sixel_apply_15bpp_dither(
1262    unsigned char *pixels,
1263    int x, int y, int width, int height,
1264    int method_for_diffuse)
1265{
1266    /* apply floyd steinberg dithering */
1267    switch (method_for_diffuse) {
1268    case SIXEL_DIFFUSE_FS:
1269        if (x < width - 1 && y < height - 1) {
1270            dither_func_fs(pixels, width);
1271        }
1272        break;
1273    case SIXEL_DIFFUSE_ATKINSON:
1274        if (x < width - 2 && y < height - 2) {
1275            dither_func_atkinson(pixels, width);
1276        }
1277        break;
1278    case SIXEL_DIFFUSE_JAJUNI:
1279        if (x < width - 2 && y < height - 2) {
1280            dither_func_jajuni(pixels, width);
1281        }
1282        break;
1283    case SIXEL_DIFFUSE_STUCKI:
1284        if (x < width - 2 && y < height - 2) {
1285            dither_func_stucki(pixels, width);
1286        }
1287        break;
1288    case SIXEL_DIFFUSE_BURKES:
1289        if (x < width - 2 && y < height - 1) {
1290            dither_func_burkes(pixels, width);
1291        }
1292        break;
1293    case SIXEL_DIFFUSE_A_DITHER:
1294        dither_func_a_dither(pixels, width, x, y);
1295        break;
1296    case SIXEL_DIFFUSE_X_DITHER:
1297        dither_func_x_dither(pixels, width, x, y);
1298        break;
1299    case SIXEL_DIFFUSE_NONE:
1300    default:
1301        dither_func_none(pixels, width);
1302        break;
1303    }
1304}
1305
1306
1307static SIXELSTATUS
1308sixel_encode_highcolor(
1309        unsigned char *pixels, int width, int height,
1310        sixel_dither_t *dither, sixel_output_t *output
1311        )
1312{
1313    SIXELSTATUS status = SIXEL_FALSE;
1314    sixel_index_t *paletted_pixels = NULL;
1315    unsigned char *normalized_pixels = NULL;
1316    /* Mark sixel line pixels which have been already drawn. */
1317    unsigned char *marks;
1318    unsigned char *rgbhit;
1319    unsigned char *rgb2pal;
1320    unsigned char palhitcount[SIXEL_PALETTE_MAX];
1321    unsigned char palstate[SIXEL_PALETTE_MAX];
1322    int output_count;
1323    int const maxcolors = 1 << 15;
1324    size_t image_size;
1325    size_t marks_size;
1326    size_t normalized_size;
1327    size_t whole_size;
1328    size_t maxcolors_size;
1329    int x, y;
1330    unsigned char *dst;
1331    unsigned char *mptr;
1332    int dirty;
1333    int mod_y;
1334    int nextpal;
1335    int threshold;
1336    int pix;
1337    int orig_height;
1338    unsigned char *pal;
1339
1340    /*
1341     * The high-color encoder keeps one palette index per input pixel
1342     * followed by color lookup tables and six scanlines of mark bytes.
1343     * Calculate all derived sizes in size_t and reject arithmetic
1344     * overflow before a wrapped value can reach the allocator.
1345     */
1346    maxcolors_size = (size_t)maxcolors;
1347    if ((size_t)height > ((size_t)-1) / (size_t)width) {
1348        sixel_helper_set_additional_message(
1349            "sixel_encode_highcolor: image size overflow.");
1350        status = SIXEL_BAD_INPUT;
1351        goto error;
1352    }
1353    image_size = (size_t)width * (size_t)height;
1354
1355    if (image_size > ((size_t)-1) / 3UL) {
1356        sixel_helper_set_additional_message(
1357            "sixel_encode_highcolor: normalized size overflow.");
1358        status = SIXEL_BAD_INPUT;
1359        goto error;
1360    }
1361    normalized_size = image_size * 3UL;
1362
1363    if ((size_t)width > ((size_t)-1) / 6UL) {
1364        sixel_helper_set_additional_message(
1365            "sixel_encode_highcolor: marks size overflow.");
1366        status = SIXEL_BAD_INPUT;
1367        goto error;
1368    }
1369    marks_size = (size_t)width * 6UL;
1370
1371    if (image_size > (size_t)-1 - maxcolors_size ||
1372        image_size + maxcolors_size > (size_t)-1 - maxcolors_size ||
1373        image_size + maxcolors_size + maxcolors_size
1374            > (size_t)-1 - marks_size) {
1375        sixel_helper_set_additional_message(
1376            "sixel_encode_highcolor: whole size overflow.");
1377        status = SIXEL_BAD_INPUT;
1378        goto error;
1379    }
1380    whole_size = image_size      /* for paletted_pixels */
1381               + maxcolors_size  /* for rgbhit */
1382               + maxcolors_size  /* for rgb2pal */
1383               + marks_size;     /* for marks */
1384
1385    if (dither->pixelformat != SIXEL_PIXELFORMAT_RGB888) {
1386        /* normalize pixelfromat */
1387        normalized_pixels = (unsigned char *)sixel_allocator_malloc(
1388            dither->allocator, normalized_size);
1389        if (normalized_pixels == NULL) {
1390            goto error;
1391        }
1392        status = sixel_helper_normalize_pixelformat(normalized_pixels,
1393                                                    &dither->pixelformat,
1394                                                    pixels,
1395                                                    dither->pixelformat,
1396                                                    width, height);
1397        if (SIXEL_FAILED(status)) {
1398            goto error;
1399        }
1400        pixels = normalized_pixels;
1401    }
1402    paletted_pixels = (sixel_index_t *)sixel_allocator_malloc(dither->allocator,
1403                                                              whole_size);
1404    if (paletted_pixels == NULL) {
1405        goto error;
1406    }
1407    rgbhit = paletted_pixels + image_size;
1408    memset(rgbhit, 0, maxcolors_size * 2UL + marks_size);
1409    rgb2pal = rgbhit + maxcolors;
1410    marks = rgb2pal + maxcolors;
1411    output_count = 0;
1412
1413next:
1414    dst = paletted_pixels;
1415    nextpal = 0;
1416    threshold = 1;
1417    dirty = 0;
1418    mptr = marks;
1419    memset(palstate, 0, sizeof(palstate));
1420    y = mod_y = 0;
1421
1422    while (1) {
1423        for (x = 0; x < width; x++, mptr++, dst++, pixels += 3) {
1424            if (*mptr) {
1425                *dst = 255;
1426            } else {
1427                sixel_apply_15bpp_dither(pixels,
1428                                         x, y, width, height,
1429                                         dither->method_for_diffuse);
1430                pix = ((pixels[0] & 0xf8) << 7) |
1431                      ((pixels[1] & 0xf8) << 2) |
1432                      ((pixels[2] >> 3) & 0x1f);
1433
1434                if (!rgbhit[pix]) {
1435                    while (1) {
1436                        if (nextpal >= 255) {
1437                            if (threshold >= 255) {
1438                                break;
1439                            } else {
1440                                threshold = (threshold == 1) ? 9: 255;
1441                                nextpal = 0;
1442                            }
1443                        } else if (palstate[nextpal] ||
1444                                 palhitcount[nextpal] > threshold) {
1445                            nextpal++;
1446                        } else {
1447                            break;
1448                        }
1449                    }
1450
1451                    if (nextpal >= 255) {
1452                        dirty = 1;
1453                        *dst = 255;
1454                    } else {
1455                        pal = dither->palette + (nextpal * 3);
1456
1457                        rgbhit[pix] = 1;
1458                        if (output_count > 0) {
1459                            rgbhit[((pal[0] & 0xf8) << 7) |
1460                                   ((pal[1] & 0xf8) << 2) |
1461                                   ((pal[2] >> 3) & 0x1f)] = 0;
1462                        }
1463                        *dst = rgb2pal[pix] = nextpal++;
1464                        *mptr = 1;
1465                        palstate[*dst] = PALETTE_CHANGE;
1466                        palhitcount[*dst] = 1;
1467                        *(pal++) = pixels[0];
1468                        *(pal++) = pixels[1];
1469                        *(pal++) = pixels[2];
1470                    }
1471                } else {
1472                    *dst = rgb2pal[pix];
1473                    *mptr = 1;
1474                    if (!palstate[*dst]) {
1475                        palstate[*dst] = PALETTE_HIT;
1476                    }
1477                    if (palhitcount[*dst] < 255) {
1478                        palhitcount[*dst]++;
1479                    }
1480                }
1481            }
1482        }
1483
1484        if (++y >= height) {
1485            if (dirty) {
1486                mod_y = 5;
1487            } else {
1488                goto end;
1489            }
1490        }
1491        if (dirty && (mod_y == 5 || y >= height)) {
1492            orig_height = height;
1493
1494            if (output_count++ == 0) {
1495                status = sixel_encode_header(width, height, output);
1496                if (SIXEL_FAILED(status)) {
1497                    goto error;
1498                }
1499            }
1500            height = y;
1501            status = sixel_encode_body(paletted_pixels,
1502                                       width,
1503                                       height,
1504                                       dither->palette,
1505                                       255,
1506                                       255,
1507                                       dither->bodyonly,
1508                                       output,
1509                                       palstate,
1510                                       dither->allocator);
1511            if (SIXEL_FAILED(status)) {
1512                goto error;
1513            }
1514            if (y >= orig_height) {
1515              goto end;
1516            }
1517            pixels -= (6 * width * 3);
1518            height = orig_height - height + 6;
1519            goto next;
1520        }
1521
1522        if (++mod_y == 6) {
1523            mptr = (unsigned char *)memset(marks, 0, marks_size);
1524            mod_y = 0;
1525        }
1526    }
1527
1528    goto next;
1529
1530end:
1531    if (output_count == 0) {
1532        status = sixel_encode_header(width, height, output);
1533        if (SIXEL_FAILED(status)) {
1534            goto error;
1535        }
1536    }
1537    status = sixel_encode_body(paletted_pixels,
1538                               width,
1539                               height,
1540                               dither->palette,
1541                               255,
1542                               255,
1543                               dither->bodyonly,
1544                               output,
1545                               palstate,
1546                               dither->allocator);
1547    if (SIXEL_FAILED(status)) {
1548        goto error;
1549    }
1550
1551    status = sixel_encode_footer(output);
1552    if (SIXEL_FAILED(status)) {
1553        goto error;
1554    }
1555
1556error:
1557    sixel_allocator_free(dither->allocator, paletted_pixels);
1558    sixel_allocator_free(dither->allocator, normalized_pixels);
1559
1560    return status;
1561}
1562
1563
1564SIXELAPI SIXELSTATUS
1565sixel_encode(
1566    unsigned char  /* in */ *pixels,   /* pixel bytes */
1567    int            /* in */ width,     /* image width */
1568    int            /* in */ height,    /* image height */
1569    int const      /* in */ depth,     /* color depth */
1570    sixel_dither_t /* in */ *dither,   /* dither context */
1571    sixel_output_t /* in */ *output)   /* output context */
1572{
1573    SIXELSTATUS status = SIXEL_FALSE;
1574
1575    (void) depth;
1576
1577    /* TODO: reference counting should be thread-safe */
1578    sixel_dither_ref(dither);
1579    sixel_output_ref(output);
1580
1581    if (width < 1) {
1582        sixel_helper_set_additional_message(
1583            "sixel_encode: bad width parameter."
1584            " (width < 1)");
1585        status = SIXEL_BAD_INPUT;
1586        goto end;
1587    }
1588
1589    if (height < 1) {
1590        sixel_helper_set_additional_message(
1591            "sixel_encode: bad height parameter."
1592            " (height < 1)");
1593        status = SIXEL_BAD_INPUT;
1594        goto end;
1595    }
1596
1597    if (width > SIXEL_WIDTH_LIMIT) {
1598        sixel_helper_set_additional_message(
1599            "sixel_encode: bad width parameter."
1600            " (width > SIXEL_WIDTH_LIMIT)");
1601        status = SIXEL_BAD_INPUT;
1602        goto end;
1603    }
1604
1605    if (height > SIXEL_HEIGHT_LIMIT) {
1606        sixel_helper_set_additional_message(
1607            "sixel_encode: bad height parameter."
1608            " (height > SIXEL_HEIGHT_LIMIT)");
1609        status = SIXEL_BAD_INPUT;
1610        goto end;
1611    }
1612
1613    if (dither->quality_mode == SIXEL_QUALITY_HIGHCOLOR) {
1614        status = sixel_encode_highcolor(pixels, width, height,
1615                                        dither, output);
1616    } else {
1617        status = sixel_encode_dither(pixels, width, height,
1618                                     dither, output);
1619    }
1620
1621end:
1622    sixel_output_unref(output);
1623    sixel_dither_unref(dither);
1624
1625    return status;
1626}
1627
1628/* emacs Local Variables:      */
1629/* emacs mode: c               */
1630/* emacs tab-width: 4          */
1631/* emacs indent-tabs-mode: nil */
1632/* emacs c-basic-offset: 4     */
1633/* emacs End:                  */
1634/* vim: set expandtab ts=4 sts=4 sw=4 : */
1635/* EOF */