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 */