master ohmu/fastfetch-openbsd / src / 3rdparty / sixel / dither.c
ohmu  ·  2026-10-15
  1/*
  2 * Copyright (c) 2014-2018 Hayaki Saito
  3 *
  4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
  5 * this software and associated documentation files (the "Software"), to deal in
  6 * the Software without restriction, including without limitation the rights to
  7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  8 * the Software, and to permit persons to whom the Software is furnished to do so,
  9 * subject to the following conditions:
 10 *
 11 * The above copyright notice and this permission notice shall be included in all
 12 * copies or substantial portions of the Software.
 13 *
 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 16 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 17 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 18 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 20 */
 21
 22#include "config.h"
 23
 24#include <stdlib.h>
 25#include <stdio.h>
 26
 27#if HAVE_MATH_H
 28# include <math.h>
 29#endif  /* HAVE_MATH_H */
 30#if HAVE_STRING_H
 31# include <string.h>
 32#endif  /* HAVE_STRING_H */
 33#if HAVE_LIMITS_H
 34# include <limits.h>
 35#endif  /* HAVE_LIMITS_H */
 36#if HAVE_INTTYPES_H
 37# include <inttypes.h>
 38#endif  /* HAVE_INTTYPES_H */
 39
 40#include "dither.h"
 41#include "quant.h"
 42#include <sixel.h>
 43
 44
 45static const unsigned char pal_mono_dark[] = {
 46    0x00, 0x00, 0x00, 0xff, 0xff, 0xff
 47};
 48
 49
 50static const unsigned char pal_mono_light[] = {
 51    0xff, 0xff, 0xff, 0x00, 0x00, 0x00
 52};
 53
 54static const unsigned char pal_gray_1bit[] = {
 55    0x00, 0x00, 0x00, 0xff, 0xff, 0xff
 56};
 57
 58
 59static const unsigned char pal_gray_2bit[] = {
 60    0x00, 0x00, 0x00, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xff, 0xff, 0xff
 61};
 62
 63
 64static const unsigned char pal_gray_4bit[] = {
 65    0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x22, 0x22, 0x22, 0x33, 0x33, 0x33,
 66    0x44, 0x44, 0x44, 0x55, 0x55, 0x55, 0x66, 0x66, 0x66, 0x77, 0x77, 0x77,
 67    0x88, 0x88, 0x88, 0x99, 0x99, 0x99, 0xaa, 0xaa, 0xaa, 0xbb, 0xbb, 0xbb,
 68    0xcc, 0xcc, 0xcc, 0xdd, 0xdd, 0xdd, 0xee, 0xee, 0xee, 0xff, 0xff, 0xff
 69};
 70
 71
 72static const unsigned char pal_gray_8bit[] = {
 73    0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
 74    0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07,
 75    0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b,
 76    0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, 0x0f,
 77    0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13,
 78    0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17,
 79    0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b,
 80    0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f,
 81    0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23,
 82    0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x26, 0x26, 0x26, 0x27, 0x27, 0x27,
 83    0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b,
 84    0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f,
 85    0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33,
 86    0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x37, 0x37, 0x37,
 87    0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b,
 88    0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f,
 89    0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43,
 90    0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x47, 0x47, 0x47,
 91    0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b,
 92    0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, 0x4f,
 93    0x50, 0x50, 0x50, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53,
 94    0x54, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57,
 95    0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b,
 96    0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x5f,
 97    0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x63,
 98    0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67,
 99    0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b,
100    0x6c, 0x6c, 0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f,
101    0x70, 0x70, 0x70, 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x73, 0x73, 0x73,
102    0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77,
103    0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7b,
104    0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x7f,
105    0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83,
106    0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87,
107    0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8b,
108    0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x8f,
109    0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93,
110    0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97,
111    0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b,
112    0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0x9f,
113    0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3,
114    0xa4, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa7, 0xa7, 0xa7,
115    0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab, 0xab,
116    0xac, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf,
117    0xb0, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb3,
118    0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb7,
119    0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9, 0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb,
120    0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf,
121    0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3,
122    0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7,
123    0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca, 0xcb, 0xcb, 0xcb,
124    0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf,
125    0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3,
126    0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7,
127    0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb,
128    0xdc, 0xdc, 0xdc, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf,
129    0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3,
130    0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe7, 0xe7, 0xe7,
131    0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb,
132    0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef,
133    0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3,
134    0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7,
135    0xf8, 0xf8, 0xf8, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb,
136    0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff
137};
138
139
140static const unsigned char pal_xterm256[] = {
141    0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x80, 0x80, 0x00,
142    0x00, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x80, 0x80, 0xc0, 0xc0, 0xc0,
143    0x80, 0x80, 0x80, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00,
144    0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
145    0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x87, 0x00, 0x00, 0xaf,
146    0x00, 0x00, 0xd7, 0x00, 0x00, 0xff, 0x00, 0x5f, 0x00, 0x00, 0x5f, 0x5f,
147    0x00, 0x5f, 0x87, 0x00, 0x5f, 0xaf, 0x00, 0x5f, 0xd7, 0x00, 0x5f, 0xff,
148    0x00, 0x87, 0x00, 0x00, 0x87, 0x5f, 0x00, 0x87, 0x87, 0x00, 0x87, 0xaf,
149    0x00, 0x87, 0xd7, 0x00, 0x87, 0xff, 0x00, 0xaf, 0x00, 0x00, 0xaf, 0x5f,
150    0x00, 0xaf, 0x87, 0x00, 0xaf, 0xaf, 0x00, 0xaf, 0xd7, 0x00, 0xaf, 0xff,
151    0x00, 0xd7, 0x00, 0x00, 0xd7, 0x5f, 0x00, 0xd7, 0x87, 0x00, 0xd7, 0xaf,
152    0x00, 0xd7, 0xd7, 0x00, 0xd7, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0x5f,
153    0x00, 0xff, 0x87, 0x00, 0xff, 0xaf, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
154    0x5f, 0x00, 0x00, 0x5f, 0x00, 0x5f, 0x5f, 0x00, 0x87, 0x5f, 0x00, 0xaf,
155    0x5f, 0x00, 0xd7, 0x5f, 0x00, 0xff, 0x5f, 0x5f, 0x00, 0x5f, 0x5f, 0x5f,
156    0x5f, 0x5f, 0x87, 0x5f, 0x5f, 0xaf, 0x5f, 0x5f, 0xd7, 0x5f, 0x5f, 0xff,
157    0x5f, 0x87, 0x00, 0x5f, 0x87, 0x5f, 0x5f, 0x87, 0x87, 0x5f, 0x87, 0xaf,
158    0x5f, 0x87, 0xd7, 0x5f, 0x87, 0xff, 0x5f, 0xaf, 0x00, 0x5f, 0xaf, 0x5f,
159    0x5f, 0xaf, 0x87, 0x5f, 0xaf, 0xaf, 0x5f, 0xaf, 0xd7, 0x5f, 0xaf, 0xff,
160    0x5f, 0xd7, 0x00, 0x5f, 0xd7, 0x5f, 0x5f, 0xd7, 0x87, 0x5f, 0xd7, 0xaf,
161    0x5f, 0xd7, 0xd7, 0x5f, 0xd7, 0xff, 0x5f, 0xff, 0x00, 0x5f, 0xff, 0x5f,
162    0x5f, 0xff, 0x87, 0x5f, 0xff, 0xaf, 0x5f, 0xff, 0xd7, 0x5f, 0xff, 0xff,
163    0x87, 0x00, 0x00, 0x87, 0x00, 0x5f, 0x87, 0x00, 0x87, 0x87, 0x00, 0xaf,
164    0x87, 0x00, 0xd7, 0x87, 0x00, 0xff, 0x87, 0x5f, 0x00, 0x87, 0x5f, 0x5f,
165    0x87, 0x5f, 0x87, 0x87, 0x5f, 0xaf, 0x87, 0x5f, 0xd7, 0x87, 0x5f, 0xff,
166    0x87, 0x87, 0x00, 0x87, 0x87, 0x5f, 0x87, 0x87, 0x87, 0x87, 0x87, 0xaf,
167    0x87, 0x87, 0xd7, 0x87, 0x87, 0xff, 0x87, 0xaf, 0x00, 0x87, 0xaf, 0x5f,
168    0x87, 0xaf, 0x87, 0x87, 0xaf, 0xaf, 0x87, 0xaf, 0xd7, 0x87, 0xaf, 0xff,
169    0x87, 0xd7, 0x00, 0x87, 0xd7, 0x5f, 0x87, 0xd7, 0x87, 0x87, 0xd7, 0xaf,
170    0x87, 0xd7, 0xd7, 0x87, 0xd7, 0xff, 0x87, 0xff, 0x00, 0x87, 0xff, 0x5f,
171    0x87, 0xff, 0x87, 0x87, 0xff, 0xaf, 0x87, 0xff, 0xd7, 0x87, 0xff, 0xff,
172    0xaf, 0x00, 0x00, 0xaf, 0x00, 0x5f, 0xaf, 0x00, 0x87, 0xaf, 0x00, 0xaf,
173    0xaf, 0x00, 0xd7, 0xaf, 0x00, 0xff, 0xaf, 0x5f, 0x00, 0xaf, 0x5f, 0x5f,
174    0xaf, 0x5f, 0x87, 0xaf, 0x5f, 0xaf, 0xaf, 0x5f, 0xd7, 0xaf, 0x5f, 0xff,
175    0xaf, 0x87, 0x00, 0xaf, 0x87, 0x5f, 0xaf, 0x87, 0x87, 0xaf, 0x87, 0xaf,
176    0xaf, 0x87, 0xd7, 0xaf, 0x87, 0xff, 0xaf, 0xaf, 0x00, 0xaf, 0xaf, 0x5f,
177    0xaf, 0xaf, 0x87, 0xaf, 0xaf, 0xaf, 0xaf, 0xaf, 0xd7, 0xaf, 0xaf, 0xff,
178    0xaf, 0xd7, 0x00, 0xaf, 0xd7, 0x5f, 0xaf, 0xd7, 0x87, 0xaf, 0xd7, 0xaf,
179    0xaf, 0xd7, 0xd7, 0xaf, 0xd7, 0xff, 0xaf, 0xff, 0x00, 0xaf, 0xff, 0x5f,
180    0xaf, 0xff, 0x87, 0xaf, 0xff, 0xaf, 0xaf, 0xff, 0xd7, 0xaf, 0xff, 0xff,
181    0xd7, 0x00, 0x00, 0xd7, 0x00, 0x5f, 0xd7, 0x00, 0x87, 0xd7, 0x00, 0xaf,
182    0xd7, 0x00, 0xd7, 0xd7, 0x00, 0xff, 0xd7, 0x5f, 0x00, 0xd7, 0x5f, 0x5f,
183    0xd7, 0x5f, 0x87, 0xd7, 0x5f, 0xaf, 0xd7, 0x5f, 0xd7, 0xd7, 0x5f, 0xff,
184    0xd7, 0x87, 0x00, 0xd7, 0x87, 0x5f, 0xd7, 0x87, 0x87, 0xd7, 0x87, 0xaf,
185    0xd7, 0x87, 0xd7, 0xd7, 0x87, 0xff, 0xd7, 0xaf, 0x00, 0xd7, 0xaf, 0x5f,
186    0xd7, 0xaf, 0x87, 0xd7, 0xaf, 0xaf, 0xd7, 0xaf, 0xd7, 0xd7, 0xaf, 0xff,
187    0xd7, 0xd7, 0x00, 0xd7, 0xd7, 0x5f, 0xd7, 0xd7, 0x87, 0xd7, 0xd7, 0xaf,
188    0xd7, 0xd7, 0xd7, 0xd7, 0xd7, 0xff, 0xd7, 0xff, 0x00, 0xd7, 0xff, 0x5f,
189    0xd7, 0xff, 0x87, 0xd7, 0xff, 0xaf, 0xd7, 0xff, 0xd7, 0xd7, 0xff, 0xff,
190    0xff, 0x00, 0x00, 0xff, 0x00, 0x5f, 0xff, 0x00, 0x87, 0xff, 0x00, 0xaf,
191    0xff, 0x00, 0xd7, 0xff, 0x00, 0xff, 0xff, 0x5f, 0x00, 0xff, 0x5f, 0x5f,
192    0xff, 0x5f, 0x87, 0xff, 0x5f, 0xaf, 0xff, 0x5f, 0xd7, 0xff, 0x5f, 0xff,
193    0xff, 0x87, 0x00, 0xff, 0x87, 0x5f, 0xff, 0x87, 0x87, 0xff, 0x87, 0xaf,
194    0xff, 0x87, 0xd7, 0xff, 0x87, 0xff, 0xff, 0xaf, 0x00, 0xff, 0xaf, 0x5f,
195    0xff, 0xaf, 0x87, 0xff, 0xaf, 0xaf, 0xff, 0xaf, 0xd7, 0xff, 0xaf, 0xff,
196    0xff, 0xd7, 0x00, 0xff, 0xd7, 0x5f, 0xff, 0xd7, 0x87, 0xff, 0xd7, 0xaf,
197    0xff, 0xd7, 0xd7, 0xff, 0xd7, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0x5f,
198    0xff, 0xff, 0x87, 0xff, 0xff, 0xaf, 0xff, 0xff, 0xd7, 0xff, 0xff, 0xff,
199    0x08, 0x08, 0x08, 0x12, 0x12, 0x12, 0x1c, 0x1c, 0x1c, 0x26, 0x26, 0x26,
200    0x30, 0x30, 0x30, 0x3a, 0x3a, 0x3a, 0x44, 0x44, 0x44, 0x4e, 0x4e, 0x4e,
201    0x58, 0x58, 0x58, 0x62, 0x62, 0x62, 0x6c, 0x6c, 0x6c, 0x76, 0x76, 0x76,
202    0x80, 0x80, 0x80, 0x8a, 0x8a, 0x8a, 0x94, 0x94, 0x94, 0x9e, 0x9e, 0x9e,
203    0xa8, 0xa8, 0xa8, 0xb2, 0xb2, 0xb2, 0xbc, 0xbc, 0xbc, 0xc6, 0xc6, 0xc6,
204    0xd0, 0xd0, 0xd0, 0xda, 0xda, 0xda, 0xe4, 0xe4, 0xe4, 0xee, 0xee, 0xee,
205};
206
207
208/*
209 * VT340 undocumented behavior regarding the color palette reported
210 * by Vertis Sidus(@vrtsds):
211 *     it loads the first fifteen colors as 1 through 15, and loads the
212 *     sixteenth color as 0.
213 */
214static const unsigned char pal_vt340_mono[] = {
215    /* 1   Gray-2   */  13 * 255 / 100, 13 * 255 / 100, 13 * 255 / 100,
216    /* 2   Gray-4   */  26 * 255 / 100, 26 * 255 / 100, 26 * 255 / 100,
217    /* 3   Gray-6   */  40 * 255 / 100, 40 * 255 / 100, 40 * 255 / 100,
218    /* 4   Gray-1   */   6 * 255 / 100,  6 * 255 / 100,  6 * 255 / 100,
219    /* 5   Gray-3   */  20 * 255 / 100, 20 * 255 / 100, 20 * 255 / 100,
220    /* 6   Gray-5   */  33 * 255 / 100, 33 * 255 / 100, 33 * 255 / 100,
221    /* 7   White 7  */  46 * 255 / 100, 46 * 255 / 100, 46 * 255 / 100,
222    /* 8   Black 0  */   0 * 255 / 100,  0 * 255 / 100,  0 * 255 / 100,
223    /* 9   Gray-2   */  13 * 255 / 100, 13 * 255 / 100, 13 * 255 / 100,
224    /* 10  Gray-4   */  26 * 255 / 100, 26 * 255 / 100, 26 * 255 / 100,
225    /* 11  Gray-6   */  40 * 255 / 100, 40 * 255 / 100, 40 * 255 / 100,
226    /* 12  Gray-1   */   6 * 255 / 100,  6 * 255 / 100,  6 * 255 / 100,
227    /* 13  Gray-3   */  20 * 255 / 100, 20 * 255 / 100, 20 * 255 / 100,
228    /* 14  Gray-5   */  33 * 255 / 100, 33 * 255 / 100, 33 * 255 / 100,
229    /* 15  White 7  */  46 * 255 / 100, 46 * 255 / 100, 46 * 255 / 100,
230    /* 0   Black    */   0 * 255 / 100,  0 * 255 / 100,  0 * 255 / 100,
231};
232
233
234static const unsigned char pal_vt340_color[] = {
235    /* 1   Blue     */  20 * 255 / 100, 20 * 255 / 100, 80 * 255 / 100,
236    /* 2   Red      */  80 * 255 / 100, 13 * 255 / 100, 13 * 255 / 100,
237    /* 3   Green    */  20 * 255 / 100, 80 * 255 / 100, 20 * 255 / 100,
238    /* 4   Magenta  */  80 * 255 / 100, 20 * 255 / 100, 80 * 255 / 100,
239    /* 5   Cyan     */  20 * 255 / 100, 80 * 255 / 100, 80 * 255 / 100,
240    /* 6   Yellow   */  80 * 255 / 100, 80 * 255 / 100, 20 * 255 / 100,
241    /* 7   Gray 50% */  53 * 255 / 100, 53 * 255 / 100, 53 * 255 / 100,
242    /* 8   Gray 25% */  26 * 255 / 100, 26 * 255 / 100, 26 * 255 / 100,
243    /* 9   Blue*    */  33 * 255 / 100, 33 * 255 / 100, 60 * 255 / 100,
244    /* 10  Red*     */  60 * 255 / 100, 26 * 255 / 100, 26 * 255 / 100,
245    /* 11  Green*   */  33 * 255 / 100, 60 * 255 / 100, 33 * 255 / 100,
246    /* 12  Magenta* */  60 * 255 / 100, 33 * 255 / 100, 60 * 255 / 100,
247    /* 13  Cyan*    */  33 * 255 / 100, 60 * 255 / 100, 60 * 255 / 100,
248    /* 14  Yellow*  */  60 * 255 / 100, 60 * 255 / 100, 33 * 255 / 100,
249    /* 15  Gray 75% */  80 * 255 / 100, 80 * 255 / 100, 80 * 255 / 100,
250    /* 0   Black    */   0 * 255 / 100,  0 * 255 / 100,  0 * 255 / 100,
251};
252
253
254/* create dither context object */
255SIXELAPI SIXELSTATUS
256sixel_dither_new(
257    sixel_dither_t    /* out */ **ppdither, /* dither object to be created */
258    int               /* in */  ncolors,    /* required colors */
259    sixel_allocator_t /* in */  *allocator) /* allocator, null if you use
260                                               default allocator */
261{
262    SIXELSTATUS status = SIXEL_FALSE;
263    size_t headsize;
264    size_t datasize;
265    size_t wholesize;
266    int quality_mode;
267
268    /* ensure given pointer is not null */
269    if (ppdither == NULL) {
270        sixel_helper_set_additional_message(
271            "sixel_dither_new: ppdither is null.");
272        status = SIXEL_BAD_ARGUMENT;
273        goto end;
274    }
275
276    if (allocator == NULL) {
277        status = sixel_allocator_new(&allocator, NULL, NULL, NULL, NULL);
278        if (SIXEL_FAILED(status)) {
279            *ppdither = NULL;
280            goto end;
281        }
282    } else {
283        sixel_allocator_ref(allocator);
284    }
285
286    if (ncolors < 0) {
287        ncolors = SIXEL_PALETTE_MAX;
288        quality_mode = SIXEL_QUALITY_HIGHCOLOR;
289    } else {
290        if (ncolors > SIXEL_PALETTE_MAX) {
291            status = SIXEL_BAD_INPUT;
292            goto end;
293        } else if (ncolors < 1) {
294            status = SIXEL_BAD_INPUT;
295            sixel_helper_set_additional_message(
296                "sixel_dither_new: palette colors must be more than 0");
297            goto end;
298        }
299        quality_mode = SIXEL_QUALITY_LOW;
300    }
301    headsize = sizeof(sixel_dither_t);
302    datasize = (size_t)(ncolors * 3);
303    wholesize = headsize + datasize;
304
305    *ppdither = (sixel_dither_t *)sixel_allocator_malloc(allocator, wholesize);
306    if (*ppdither == NULL) {
307        sixel_allocator_unref(allocator);
308        sixel_helper_set_additional_message(
309            "sixel_dither_new: sixel_allocator_malloc() failed.");
310        status = SIXEL_BAD_ALLOCATION;
311        goto end;
312    }
313
314    (*ppdither)->ref = 1;
315    (*ppdither)->palette = (unsigned char*)(*ppdither + 1);
316    (*ppdither)->cachetable = NULL;
317    (*ppdither)->reqcolors = ncolors;
318    (*ppdither)->ncolors = ncolors;
319    (*ppdither)->origcolors = (-1);
320    (*ppdither)->keycolor = (-1);
321    (*ppdither)->optimized = 0;
322    (*ppdither)->optimize_palette = 0;
323    (*ppdither)->complexion = 1;
324    (*ppdither)->bodyonly = 0;
325    (*ppdither)->method_for_largest = SIXEL_LARGE_NORM;
326    (*ppdither)->method_for_rep = SIXEL_REP_CENTER_BOX;
327    (*ppdither)->method_for_diffuse = SIXEL_DIFFUSE_FS;
328    (*ppdither)->quality_mode = quality_mode;
329    (*ppdither)->pixelformat = SIXEL_PIXELFORMAT_RGB888;
330    (*ppdither)->allocator = allocator;
331
332    status = SIXEL_OK;
333
334end:
335    return status;
336}
337
338
339/* create dither context object (deprecated) */
340SIXELAPI sixel_dither_t *
341sixel_dither_create(
342    int     /* in */ ncolors)
343{
344    SIXELSTATUS status = SIXEL_FALSE;
345    sixel_dither_t *dither = NULL;
346
347    status = sixel_dither_new(&dither, ncolors, NULL);
348    if (SIXEL_FAILED(status)) {
349        goto end;
350    }
351
352end:
353    return dither;
354}
355
356
357SIXELAPI void
358sixel_dither_destroy(
359    sixel_dither_t  /* in */ *dither)
360{
361    sixel_allocator_t *allocator;
362
363    if (dither) {
364        allocator = dither->allocator;
365        sixel_allocator_free(allocator, dither->cachetable);
366        dither->cachetable = NULL;
367        sixel_allocator_free(allocator, dither);
368        sixel_allocator_unref(allocator);
369    }
370}
371
372
373SIXELAPI void
374sixel_dither_ref(
375    sixel_dither_t  /* in */ *dither)
376{
377    /* TODO: be thread safe */
378    ++dither->ref;
379}
380
381
382SIXELAPI void
383sixel_dither_unref(
384    sixel_dither_t  /* in */ *dither)
385{
386    /* TODO: be thread safe */
387    if (dither != NULL && --dither->ref == 0) {
388        sixel_dither_destroy(dither);
389    }
390}
391
392
393SIXELAPI sixel_dither_t *
394sixel_dither_get(
395    int     /* in */ builtin_dither)
396{
397    SIXELSTATUS status = SIXEL_FALSE;
398    unsigned char *palette;
399    int ncolors;
400    int keycolor;
401    sixel_dither_t *dither = NULL;
402
403    switch (builtin_dither) {
404    case SIXEL_BUILTIN_MONO_DARK:
405        ncolors = 2;
406        palette = (unsigned char *)pal_mono_dark;
407        keycolor = 0;
408        break;
409    case SIXEL_BUILTIN_MONO_LIGHT:
410        ncolors = 2;
411        palette = (unsigned char *)pal_mono_light;
412        keycolor = 0;
413        break;
414    case SIXEL_BUILTIN_XTERM16:
415        ncolors = 16;
416        palette = (unsigned char *)pal_xterm256;
417        keycolor = (-1);
418        break;
419    case SIXEL_BUILTIN_XTERM256:
420        ncolors = 256;
421        palette = (unsigned char *)pal_xterm256;
422        keycolor = (-1);
423        break;
424    case SIXEL_BUILTIN_VT340_MONO:
425        ncolors = 16;
426        palette = (unsigned char *)pal_vt340_mono;
427        keycolor = (-1);
428        break;
429    case SIXEL_BUILTIN_VT340_COLOR:
430        ncolors = 16;
431        palette = (unsigned char *)pal_vt340_color;
432        keycolor = (-1);
433        break;
434    case SIXEL_BUILTIN_G1:
435        ncolors = 2;
436        palette = (unsigned char *)pal_gray_1bit;
437        keycolor = (-1);
438        break;
439    case SIXEL_BUILTIN_G2:
440        ncolors = 4;
441        palette = (unsigned char *)pal_gray_2bit;
442        keycolor = (-1);
443        break;
444    case SIXEL_BUILTIN_G4:
445        ncolors = 16;
446        palette = (unsigned char *)pal_gray_4bit;
447        keycolor = (-1);
448        break;
449    case SIXEL_BUILTIN_G8:
450        ncolors = 256;
451        palette = (unsigned char *)pal_gray_8bit;
452        keycolor = (-1);
453        break;
454    default:
455        goto end;
456    }
457
458    status = sixel_dither_new(&dither, ncolors, NULL);
459    if (SIXEL_FAILED(status)) {
460        dither = NULL;
461        goto end;
462    }
463
464    dither->palette = palette;
465    dither->keycolor = keycolor;
466    dither->optimized = 1;
467    dither->optimize_palette = 0;
468
469end:
470    return dither;
471}
472
473
474static void
475sixel_dither_set_method_for_largest(
476    sixel_dither_t  /* in */ *dither,
477    int             /* in */ method_for_largest)
478{
479    if (method_for_largest == SIXEL_LARGE_AUTO) {
480        method_for_largest = SIXEL_LARGE_NORM;
481    }
482    dither->method_for_largest = method_for_largest;
483}
484
485
486static void
487sixel_dither_set_method_for_rep(
488    sixel_dither_t  /* in */ *dither,
489    int             /* in */ method_for_rep)
490{
491    if (method_for_rep == SIXEL_REP_AUTO) {
492        method_for_rep = SIXEL_REP_CENTER_BOX;
493    }
494    dither->method_for_rep = method_for_rep;
495}
496
497
498static void
499sixel_dither_set_quality_mode(
500    sixel_dither_t  /* in */  *dither,
501    int             /* in */  quality_mode)
502{
503    if (quality_mode == SIXEL_QUALITY_AUTO) {
504        if (dither->ncolors <= 8) {
505            quality_mode = SIXEL_QUALITY_HIGH;
506        } else {
507            quality_mode = SIXEL_QUALITY_LOW;
508        }
509    }
510    dither->quality_mode = quality_mode;
511}
512
513
514SIXELAPI SIXELSTATUS
515sixel_dither_initialize(
516    sixel_dither_t  /* in */ *dither,
517    unsigned char   /* in */ *data,
518    int             /* in */ width,
519    int             /* in */ height,
520    int             /* in */ pixelformat,
521    int             /* in */ method_for_largest,
522    int             /* in */ method_for_rep,
523    int             /* in */ quality_mode)
524{
525    unsigned char *buf = NULL;
526    unsigned char *normalized_pixels = NULL;
527    unsigned char *input_pixels;
528    SIXELSTATUS status = SIXEL_FALSE;
529
530    /* ensure dither object is not null */
531    if (dither == NULL) {
532        sixel_helper_set_additional_message(
533            "sixel_dither_new: dither is null.");
534        status = SIXEL_BAD_ARGUMENT;
535        goto end;
536    }
537
538    /* increment ref count */
539    sixel_dither_ref(dither);
540
541    sixel_dither_set_pixelformat(dither, pixelformat);
542
543    switch (pixelformat) {
544    case SIXEL_PIXELFORMAT_RGB888:
545        input_pixels = data;
546        break;
547    default:
548        /* normalize pixelformat */
549        normalized_pixels
550            = (unsigned char *)sixel_allocator_malloc(dither->allocator, (size_t)(width * height * 3));
551        if (normalized_pixels == NULL) {
552            sixel_helper_set_additional_message(
553                "sixel_dither_initialize: sixel_allocator_malloc() failed.");
554            status = SIXEL_BAD_ALLOCATION;
555            goto end;
556        }
557
558        status = sixel_helper_normalize_pixelformat(
559            normalized_pixels,
560            &pixelformat,
561            data,
562            pixelformat,
563            width,
564            height);
565        if (SIXEL_FAILED(status)) {
566            goto end;
567        }
568        input_pixels = normalized_pixels;
569        break;
570    }
571
572    sixel_dither_set_method_for_largest(dither, method_for_largest);
573    sixel_dither_set_method_for_rep(dither, method_for_rep);
574    sixel_dither_set_quality_mode(dither, quality_mode);
575
576    status = sixel_quant_make_palette(&buf,
577                                      input_pixels,
578                                      (unsigned int)(width * height * 3),
579                                      SIXEL_PIXELFORMAT_RGB888,
580                                      (unsigned int)dither->reqcolors,
581                                      (unsigned int *)&dither->ncolors,
582                                      (unsigned int *)&dither->origcolors,
583                                      dither->method_for_largest,
584                                      dither->method_for_rep,
585                                      dither->quality_mode,
586                                      dither->allocator);
587    if (SIXEL_FAILED(status)) {
588        goto end;
589    }
590    memcpy(dither->palette, buf, (size_t)(dither->ncolors * 3));
591
592    dither->optimized = 1;
593    if (dither->origcolors <= dither->ncolors) {
594        dither->method_for_diffuse = SIXEL_DIFFUSE_NONE;
595    }
596
597    sixel_quant_free_palette(buf, dither->allocator);
598    status = SIXEL_OK;
599
600end:
601    free(normalized_pixels);
602
603    /* decrement ref count */
604    sixel_dither_unref(dither);
605
606    return status;
607}
608
609
610/* set diffusion type, choose from enum methodForDiffuse */
611SIXELAPI void
612sixel_dither_set_diffusion_type(
613    sixel_dither_t  /* in */ *dither,
614    int             /* in */ method_for_diffuse)
615{
616    if (method_for_diffuse == SIXEL_DIFFUSE_AUTO) {
617        if (dither->ncolors > 16) {
618            method_for_diffuse = SIXEL_DIFFUSE_FS;
619        } else {
620            method_for_diffuse = SIXEL_DIFFUSE_ATKINSON;
621        }
622    }
623    dither->method_for_diffuse = method_for_diffuse;
624}
625
626
627/* get number of palette colors */
628SIXELAPI int
629sixel_dither_get_num_of_palette_colors(
630    sixel_dither_t  /* in */ *dither)
631{
632    return dither->ncolors;
633}
634
635
636/* get number of histogram colors */
637SIXELAPI int
638sixel_dither_get_num_of_histogram_colors(
639    sixel_dither_t /* in */ *dither)  /* dither context object */
640{
641    return dither->origcolors;
642}
643
644
645/* typoed: remained for keeping compatibility */
646SIXELAPI int
647sixel_dither_get_num_of_histgram_colors(
648    sixel_dither_t /* in */ *dither)  /* dither context object */
649{
650    return sixel_dither_get_num_of_histogram_colors(dither);
651}
652
653
654/* get palette */
655SIXELAPI unsigned char *
656sixel_dither_get_palette(
657    sixel_dither_t /* in */ *dither)  /* dither context object */
658{
659    return dither->palette;
660}
661
662
663/* set palette */
664SIXELAPI void
665sixel_dither_set_palette(
666    sixel_dither_t /* in */ *dither,   /* dither context object */
667    unsigned char  /* in */ *palette)
668{
669    memcpy(dither->palette, palette, (size_t)(dither->ncolors * 3));
670}
671
672
673/* set the factor of complexion color correcting */
674SIXELAPI void
675sixel_dither_set_complexion_score(
676    sixel_dither_t /* in */ *dither,  /* dither context object */
677    int            /* in */ score)    /* complexion score (>= 1) */
678{
679    dither->complexion = score;
680}
681
682
683/* set whether omitting palette difinition */
684SIXELAPI void
685sixel_dither_set_body_only(
686    sixel_dither_t /* in */ *dither,     /* dither context object */
687    int            /* in */ bodyonly)    /* 0: output palette section
688                                            1: do not output palette section  */
689{
690    dither->bodyonly = bodyonly;
691}
692
693
694/* set whether optimize palette size */
695SIXELAPI void
696sixel_dither_set_optimize_palette(
697    sixel_dither_t /* in */ *dither,   /* dither context object */
698    int            /* in */ do_opt)    /* 0: optimize palette size
699                                          1: don't optimize palette size */
700{
701    dither->optimize_palette = do_opt;
702}
703
704
705/* set pixelformat */
706SIXELAPI void
707sixel_dither_set_pixelformat(
708    sixel_dither_t /* in */ *dither,     /* dither context object */
709    int            /* in */ pixelformat) /* one of enum pixelFormat */
710{
711    dither->pixelformat = pixelformat;
712}
713
714
715/* set transparent */
716SIXELAPI void
717sixel_dither_set_transparent(
718    sixel_dither_t /* in */ *dither,      /* dither context object */
719    int            /* in */ transparent)  /* transparent color index */
720{
721    dither->keycolor = transparent;
722}
723
724
725/* set transparent */
726SIXELAPI sixel_index_t *
727sixel_dither_apply_palette(
728    sixel_dither_t  /* in */ *dither,
729    unsigned char   /* in */ *pixels,
730    int             /* in */ width,
731    int             /* in */ height)
732{
733    SIXELSTATUS status = SIXEL_FALSE;
734    size_t bufsize;
735    sixel_index_t *dest = NULL;
736    int ncolors;
737    unsigned char *normalized_pixels = NULL;
738    unsigned char *input_pixels;
739
740    /* ensure dither object is not null */
741    if (dither == NULL) {
742        sixel_helper_set_additional_message(
743            "sixel_dither_apply_palette: dither is null.");
744        status = SIXEL_BAD_ARGUMENT;
745        goto end;
746    }
747
748    sixel_dither_ref(dither);
749
750    bufsize = (size_t)(width * height) * sizeof(sixel_index_t);
751    dest = (sixel_index_t *)sixel_allocator_malloc(dither->allocator, bufsize);
752    if (dest == NULL) {
753        sixel_helper_set_additional_message(
754            "sixel_dither_new: sixel_allocator_malloc() failed.");
755        status = SIXEL_BAD_ALLOCATION;
756        goto end;
757    }
758
759    /* if quality_mode is full, do not use palette caching */
760    if (dither->quality_mode == SIXEL_QUALITY_FULL) {
761        dither->optimized = 0;
762    }
763
764    if (dither->cachetable == NULL && dither->optimized) {
765        if (dither->palette != pal_mono_dark && dither->palette != pal_mono_light) {
766            dither->cachetable = (unsigned short *)sixel_allocator_calloc(dither->allocator,
767                                                                          (size_t)(1 << 3 * 5),
768                                                                          sizeof(unsigned short));
769            if (dither->cachetable == NULL) {
770                sixel_helper_set_additional_message(
771                    "sixel_dither_new: sixel_allocator_calloc() failed.");
772                status = SIXEL_BAD_ALLOCATION;
773                goto end;
774            }
775        }
776    }
777
778    if (dither->pixelformat != SIXEL_PIXELFORMAT_RGB888) {
779        /* normalize pixelformat */
780        normalized_pixels
781            = (unsigned char *)sixel_allocator_malloc(dither->allocator, (size_t)(width * height * 3));
782        if (normalized_pixels == NULL) {
783            sixel_helper_set_additional_message(
784                "sixel_dither_new: sixel_allocator_malloc() failed.");
785            status = SIXEL_BAD_ALLOCATION;
786            goto end;
787        }
788        status = sixel_helper_normalize_pixelformat(normalized_pixels,
789                                                    &dither->pixelformat,
790                                                    pixels, dither->pixelformat,
791                                                    width, height);
792        if (SIXEL_FAILED(status)) {
793            goto end;
794        }
795        input_pixels = normalized_pixels;
796    } else {
797        input_pixels = pixels;
798    }
799
800    status = sixel_quant_apply_palette(dest,
801                                       input_pixels,
802                                       width, height, 3,
803                                       dither->palette,
804                                       dither->ncolors,
805                                       dither->method_for_diffuse,
806                                       dither->optimized,
807                                       dither->optimize_palette,
808                                       dither->complexion,
809                                       dither->cachetable,
810                                       &ncolors,
811                                       dither->allocator);
812    if (SIXEL_FAILED(status)) {
813        free(dest);
814        dest = NULL;
815        goto end;
816    }
817
818    dither->ncolors = ncolors;
819
820end:
821    free(normalized_pixels);
822    sixel_dither_unref(dither);
823    return dest;
824}
825
826
827#if HAVE_TESTS
828static int
829test1(void)
830{
831    sixel_dither_t *dither = NULL;
832    int nret = EXIT_FAILURE;
833
834#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
835#  pragma GCC diagnostic push
836#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
837#endif
838    dither = sixel_dither_create(2);
839#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
840#  pragma GCC diagnostic pop
841#endif
842    if (dither == NULL) {
843        goto error;
844    }
845    sixel_dither_ref(dither);
846    sixel_dither_unref(dither);
847    nret = EXIT_SUCCESS;
848
849error:
850    sixel_dither_unref(dither);
851    return nret;
852}
853
854static int
855test2(void)
856{
857    sixel_dither_t *dither = NULL;
858    int colors;
859    int nret = EXIT_FAILURE;
860
861#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
862#  pragma GCC diagnostic push
863#  pragma GCC diagnostic ignored "-Wdeprecated-declarations"
864#endif
865    dither = sixel_dither_create(INT_MAX);
866#if HAVE_DIAGNOSTIC_DEPRECATED_DECLARATIONS
867#  pragma GCC diagnostic pop
868#endif
869    if (dither == NULL) {
870        goto error;
871    }
872    sixel_dither_set_body_only(dither, 1);
873    colors = sixel_dither_get_num_of_histogram_colors(dither);
874    if (colors != -1) {
875        goto error;
876    }
877    nret = EXIT_SUCCESS;
878
879error:
880    sixel_dither_unref(dither);
881    return nret;
882}
883
884
885SIXELAPI int
886sixel_dither_tests_main(void)
887{
888    int nret = EXIT_FAILURE;
889    size_t i;
890    typedef int (* testcase)(void);
891
892    static testcase const testcases[] = {
893        test1,
894        test2,
895    };
896
897    for (i = 0; i < sizeof(testcases) / sizeof(testcase); ++i) {
898        nret = testcases[i]();
899        if (nret != EXIT_SUCCESS) {
900            goto error;
901        }
902    }
903
904    nret = EXIT_SUCCESS;
905
906error:
907    return nret;
908}
909#endif  /* HAVE_TESTS */
910
911/* emacs Local Variables:      */
912/* emacs mode: c               */
913/* emacs tab-width: 4          */
914/* emacs indent-tabs-mode: nil */
915/* emacs c-basic-offset: 4     */
916/* emacs End:                  */
917/* vim: set expandtab ts=4 sts=4 sw=4 : */
918/* EOF */