KSyntaxHighlighting

ansihighlighter.cpp
1 /*
2  SPDX-FileCopyrightText: 2020 Jonathan Poelen <[email protected]>
3 
4  SPDX-License-Identifier: MIT
5 */
6 
7 #include "ansihighlighter.h"
8 #include "context_p.h"
9 #include "definition.h"
10 #include "definition_p.h"
11 #include "format.h"
12 #include "ksyntaxhighlighting_logging.h"
13 #include "state.h"
14 #include "state_p.h"
15 #include "theme.h"
16 
17 #include <QColor>
18 #include <QFile>
19 #include <QFileInfo>
20 #include <QHash>
21 #include <QTextStream>
22 
23 #include <cmath>
24 #include <vector>
25 
26 using namespace KSyntaxHighlighting;
27 
28 namespace
29 {
30 struct Lab {
31  double L;
32  double a;
33  double b;
34 };
35 
36 // clang-format off
37  // xterm color reference
38  // constexpr Rgb888 xterm256Colors[] {
39  // {0x00, 0x00, 0x00}, {0x80, 0x00, 0x00}, {0x00, 0x80, 0x00}, {0x80, 0x80, 0x00},
40  // {0x00, 0x00, 0x80}, {0x80, 0x00, 0x80}, {0x00, 0x80, 0x80}, {0xc0, 0xc0, 0xc0},
41  // {0x80, 0x80, 0x80}, {0xff, 0x00, 0x00}, {0x00, 0xff, 0x00}, {0xff, 0xff, 0x00},
42  // {0x00, 0x00, 0xff}, {0xff, 0x00, 0xff}, {0x00, 0xff, 0xff}, {0xff, 0xff, 0xff},
43  // {0x00, 0x00, 0x00}, {0x00, 0x00, 0x5f}, {0x00, 0x00, 0x87}, {0x00, 0x00, 0xaf},
44  // {0x00, 0x00, 0xd7}, {0x00, 0x00, 0xff}, {0x00, 0x5f, 0x00}, {0x00, 0x5f, 0x5f},
45  // {0x00, 0x5f, 0x87}, {0x00, 0x5f, 0xaf}, {0x00, 0x5f, 0xd7}, {0x00, 0x5f, 0xff},
46  // {0x00, 0x87, 0x00}, {0x00, 0x87, 0x5f}, {0x00, 0x87, 0x87}, {0x00, 0x87, 0xaf},
47  // {0x00, 0x87, 0xd7}, {0x00, 0x87, 0xff}, {0x00, 0xaf, 0x00}, {0x00, 0xaf, 0x5f},
48  // {0x00, 0xaf, 0x87}, {0x00, 0xaf, 0xaf}, {0x00, 0xaf, 0xd7}, {0x00, 0xaf, 0xff},
49  // {0x00, 0xd7, 0x00}, {0x00, 0xd7, 0x5f}, {0x00, 0xd7, 0x87}, {0x00, 0xd7, 0xaf},
50  // {0x00, 0xd7, 0xd7}, {0x00, 0xd7, 0xff}, {0x00, 0xff, 0x00}, {0x00, 0xff, 0x5f},
51  // {0x00, 0xff, 0x87}, {0x00, 0xff, 0xaf}, {0x00, 0xff, 0xd7}, {0x00, 0xff, 0xff},
52  // {0x5f, 0x00, 0x00}, {0x5f, 0x00, 0x5f}, {0x5f, 0x00, 0x87}, {0x5f, 0x00, 0xaf},
53  // {0x5f, 0x00, 0xd7}, {0x5f, 0x00, 0xff}, {0x5f, 0x5f, 0x00}, {0x5f, 0x5f, 0x5f},
54  // {0x5f, 0x5f, 0x87}, {0x5f, 0x5f, 0xaf}, {0x5f, 0x5f, 0xd7}, {0x5f, 0x5f, 0xff},
55  // {0x5f, 0x87, 0x00}, {0x5f, 0x87, 0x5f}, {0x5f, 0x87, 0x87}, {0x5f, 0x87, 0xaf},
56  // {0x5f, 0x87, 0xd7}, {0x5f, 0x87, 0xff}, {0x5f, 0xaf, 0x00}, {0x5f, 0xaf, 0x5f},
57  // {0x5f, 0xaf, 0x87}, {0x5f, 0xaf, 0xaf}, {0x5f, 0xaf, 0xd7}, {0x5f, 0xaf, 0xff},
58  // {0x5f, 0xd7, 0x00}, {0x5f, 0xd7, 0x5f}, {0x5f, 0xd7, 0x87}, {0x5f, 0xd7, 0xaf},
59  // {0x5f, 0xd7, 0xd7}, {0x5f, 0xd7, 0xff}, {0x5f, 0xff, 0x00}, {0x5f, 0xff, 0x5f},
60  // {0x5f, 0xff, 0x87}, {0x5f, 0xff, 0xaf}, {0x5f, 0xff, 0xd7}, {0x5f, 0xff, 0xff},
61  // {0x87, 0x00, 0x00}, {0x87, 0x00, 0x5f}, {0x87, 0x00, 0x87}, {0x87, 0x00, 0xaf},
62  // {0x87, 0x00, 0xd7}, {0x87, 0x00, 0xff}, {0x87, 0x5f, 0x00}, {0x87, 0x5f, 0x5f},
63  // {0x87, 0x5f, 0x87}, {0x87, 0x5f, 0xaf}, {0x87, 0x5f, 0xd7}, {0x87, 0x5f, 0xff},
64  // {0x87, 0x87, 0x00}, {0x87, 0x87, 0x5f}, {0x87, 0x87, 0x87}, {0x87, 0x87, 0xaf},
65  // {0x87, 0x87, 0xd7}, {0x87, 0x87, 0xff}, {0x87, 0xaf, 0x00}, {0x87, 0xaf, 0x5f},
66  // {0x87, 0xaf, 0x87}, {0x87, 0xaf, 0xaf}, {0x87, 0xaf, 0xd7}, {0x87, 0xaf, 0xff},
67  // {0x87, 0xd7, 0x00}, {0x87, 0xd7, 0x5f}, {0x87, 0xd7, 0x87}, {0x87, 0xd7, 0xaf},
68  // {0x87, 0xd7, 0xd7}, {0x87, 0xd7, 0xff}, {0x87, 0xff, 0x00}, {0x87, 0xff, 0x5f},
69  // {0x87, 0xff, 0x87}, {0x87, 0xff, 0xaf}, {0x87, 0xff, 0xd7}, {0x87, 0xff, 0xff},
70  // {0xaf, 0x00, 0x00}, {0xaf, 0x00, 0x5f}, {0xaf, 0x00, 0x87}, {0xaf, 0x00, 0xaf},
71  // {0xaf, 0x00, 0xd7}, {0xaf, 0x00, 0xff}, {0xaf, 0x5f, 0x00}, {0xaf, 0x5f, 0x5f},
72  // {0xaf, 0x5f, 0x87}, {0xaf, 0x5f, 0xaf}, {0xaf, 0x5f, 0xd7}, {0xaf, 0x5f, 0xff},
73  // {0xaf, 0x87, 0x00}, {0xaf, 0x87, 0x5f}, {0xaf, 0x87, 0x87}, {0xaf, 0x87, 0xaf},
74  // {0xaf, 0x87, 0xd7}, {0xaf, 0x87, 0xff}, {0xaf, 0xaf, 0x00}, {0xaf, 0xaf, 0x5f},
75  // {0xaf, 0xaf, 0x87}, {0xaf, 0xaf, 0xaf}, {0xaf, 0xaf, 0xd7}, {0xaf, 0xaf, 0xff},
76  // {0xaf, 0xd7, 0x00}, {0xaf, 0xd7, 0x5f}, {0xaf, 0xd7, 0x87}, {0xaf, 0xd7, 0xaf},
77  // {0xaf, 0xd7, 0xd7}, {0xaf, 0xd7, 0xff}, {0xaf, 0xff, 0x00}, {0xaf, 0xff, 0x5f},
78  // {0xaf, 0xff, 0x87}, {0xaf, 0xff, 0xaf}, {0xaf, 0xff, 0xd7}, {0xaf, 0xff, 0xff},
79  // {0xd7, 0x00, 0x00}, {0xd7, 0x00, 0x5f}, {0xd7, 0x00, 0x87}, {0xd7, 0x00, 0xaf},
80  // {0xd7, 0x00, 0xd7}, {0xd7, 0x00, 0xff}, {0xd7, 0x5f, 0x00}, {0xd7, 0x5f, 0x5f},
81  // {0xd7, 0x5f, 0x87}, {0xd7, 0x5f, 0xaf}, {0xd7, 0x5f, 0xd7}, {0xd7, 0x5f, 0xff},
82  // {0xd7, 0x87, 0x00}, {0xd7, 0x87, 0x5f}, {0xd7, 0x87, 0x87}, {0xd7, 0x87, 0xaf},
83  // {0xd7, 0x87, 0xd7}, {0xd7, 0x87, 0xff}, {0xd7, 0xaf, 0x00}, {0xd7, 0xaf, 0x5f},
84  // {0xd7, 0xaf, 0x87}, {0xd7, 0xaf, 0xaf}, {0xd7, 0xaf, 0xd7}, {0xd7, 0xaf, 0xff},
85  // {0xd7, 0xd7, 0x00}, {0xd7, 0xd7, 0x5f}, {0xd7, 0xd7, 0x87}, {0xd7, 0xd7, 0xaf},
86  // {0xd7, 0xd7, 0xd7}, {0xd7, 0xd7, 0xff}, {0xd7, 0xff, 0x00}, {0xd7, 0xff, 0x5f},
87  // {0xd7, 0xff, 0x87}, {0xd7, 0xff, 0xaf}, {0xd7, 0xff, 0xd7}, {0xd7, 0xff, 0xff},
88  // {0xff, 0x00, 0x00}, {0xff, 0x00, 0x5f}, {0xff, 0x00, 0x87}, {0xff, 0x00, 0xaf},
89  // {0xff, 0x00, 0xd7}, {0xff, 0x00, 0xff}, {0xff, 0x5f, 0x00}, {0xff, 0x5f, 0x5f},
90  // {0xff, 0x5f, 0x87}, {0xff, 0x5f, 0xaf}, {0xff, 0x5f, 0xd7}, {0xff, 0x5f, 0xff},
91  // {0xff, 0x87, 0x00}, {0xff, 0x87, 0x5f}, {0xff, 0x87, 0x87}, {0xff, 0x87, 0xaf},
92  // {0xff, 0x87, 0xd7}, {0xff, 0x87, 0xff}, {0xff, 0xaf, 0x00}, {0xff, 0xaf, 0x5f},
93  // {0xff, 0xaf, 0x87}, {0xff, 0xaf, 0xaf}, {0xff, 0xaf, 0xd7}, {0xff, 0xaf, 0xff},
94  // {0xff, 0xd7, 0x00}, {0xff, 0xd7, 0x5f}, {0xff, 0xd7, 0x87}, {0xff, 0xd7, 0xaf},
95  // {0xff, 0xd7, 0xd7}, {0xff, 0xd7, 0xff}, {0xff, 0xff, 0x00}, {0xff, 0xff, 0x5f},
96  // {0xff, 0xff, 0x87}, {0xff, 0xff, 0xaf}, {0xff, 0xff, 0xd7}, {0xff, 0xff, 0xff},
97  // {0x08, 0x08, 0x08}, {0x12, 0x12, 0x12}, {0x1c, 0x1c, 0x1c}, {0x26, 0x26, 0x26},
98  // {0x30, 0x30, 0x30}, {0x3a, 0x3a, 0x3a}, {0x44, 0x44, 0x44}, {0x4e, 0x4e, 0x4e},
99  // {0x58, 0x58, 0x58}, {0x62, 0x62, 0x62}, {0x6c, 0x6c, 0x6c}, {0x76, 0x76, 0x76},
100  // {0x80, 0x80, 0x80}, {0x8a, 0x8a, 0x8a}, {0x94, 0x94, 0x94}, {0x9e, 0x9e, 0x9e},
101  // {0xa8, 0xa8, 0xa8}, {0xb2, 0xb2, 0xb2}, {0xbc, 0xbc, 0xbc}, {0xc6, 0xc6, 0xc6},
102  // {0xd0, 0xd0, 0xd0}, {0xda, 0xda, 0xda}, {0xe4, 0xe4, 0xe4}, {0xee, 0xee, 0xee},
103  // };
104 
105  // xterm color represented in Oklab
106  // see rgbToOklab()
107  constexpr Lab xterm240_Oklabs[] {
108  // ignore the first 16 colors as they are unpredictable (user configurable)
109  // {0x0p+0, 0x0p+0, 0x0p+0},
110  // {0x1.2d5e6bee2c4f6p+5, 0x1.af99c042ea40cp+3, 0x1.e2f6ba84d2d25p+2},
111  // {0x1.9fcc4f3622914p+5, -0x1.c105bf1d2d218p+3, 0x1.586870aec30a4p+3},
112  // {0x1.d089126c75579p+5, -0x1.12107f2e3119p+2, 0x1.7d020b82d4b9cp+3},
113  // {0x1.b1ce15c4fcb51p+4, -0x1.f203762eb1242p+0, -0x1.2b150ae3c14bep+4},
114  // {0x1.50bc446f31833p+5, 0x1.078a1150b431ap+4, -0x1.44d35b3de7eafp+3},
115  // {0x1.b27d96eb8f471p+5, -0x1.1ee8867b0e065p+3, -0x1.2f2f18261c69ap+1},
116  // {0x1.431e523cc2f4dp+6, -0x1.ad694c777b8p-11, -0x1.c7c3ea0c32ep-8},
117  // {0x1.dfe5855ae1528p+5, -0x1.3ee1ad40618p-11, -0x1.5273b9784a3p-8},
118  // {0x1.f663baac570efp+5, 0x1.67be9f690c994p+4, 0x1.928e76c3750aep+3},
119  // {0x1.5a92b1ff8af32p+6, -0x1.76441609cfb3ap+4, 0x1.1f1186319beaap+4},
120  // {0x1.83323c984ee7ap+6, -0x1.c8df58716d4cbp+2, 0x1.3d9335b5d20f9p+4},
121  // {0x1.69950098864afp+5, -0x1.9f19c42a8c674p+1, -0x1.f293e325bec2ep+4},
122  // {0x1.18ac5c68852cdp+6, 0x1.b753a9bd5dbeep+4, -0x1.0ebf0dff35bfbp+4},
123  // {0x1.6a27499e4d3d6p+6, -0x1.de4892c062f8p+3, -0x1.f96a59bc9de4cp+1},
124  // {0x1.8ffffbb77c76ap+6, -0x1.09cab717214p-10, -0x1.1a1aa7765a7p-7},
125  // 240 colors mode
126  {0x0p+0, 0x0p+0, 0x0p+0},
127  {0x1.5f181b2779cap+4, -0x1.930f78e22f09ap+0, -0x1.e41dbddfca08ap+3},
128  {0x1.c2d3be821f882p+4, -0x1.02c70dd8af008p+1, -0x1.36d1623919ffp+4},
129  {0x1.10a39beeb2926p+5, -0x1.38fe38b7dab01p+1, -0x1.77efa95d520b4p+4},
130  {0x1.3de43fe8d92efp+5, -0x1.6cf188320fff2p+1, -0x1.b655790a27192p+4},
131  {0x1.69950098864afp+5, -0x1.9f19c42a8c674p+1, -0x1.f293e325bec2ep+4},
132  {0x1.50853f46a9f9ep+5, -0x1.6b6901a80404fp+3, 0x1.16bdec11e60d8p+3},
133  {0x1.5fa625f2c3fbcp+5, -0x1.d0691ed5aa7ap+2, -0x1.eac16e8cb9241p+0},
134  {0x1.6f4222fc3f0dbp+5, -0x1.61b0e7ffa5e8ap+2, -0x1.05e8906ee23d7p+3},
135  {0x1.83e99e6187f46p+5, -0x1.1a61c98b895p+2, -0x1.c3da094bbcf2fp+3},
136  {0x1.9ca47689a503dp+5, -0x1.e9259e3439104p+1, -0x1.396812c634de3p+4},
137  {0x1.b872b55db6144p+5, -0x1.ccd50a2fd6662p+1, -0x1.89dec898ec996p+4},
138  {0x1.b01d15d276ef1p+5, -0x1.d2a4448f6ccacp+3, 0x1.65ec167dcb488p+3},
139  {0x1.b9760adf5c444p+5, -0x1.6a3760af4b6c4p+3, 0x1.a26bb322495e2p+1},
140  {0x1.c38a22a31944p+5, -0x1.2a2a93c4b741p+3, -0x1.3b14a376ffbecp+1},
141  {0x1.d185a36cfcd6ep+5, -0x1.e760f29e8fcb4p+2, -0x1.0bf936f2a6743p+3},
142  {0x1.e321d754f2b44p+5, -0x1.95a749a95debp+2, -0x1.c3928e31b9cf8p+3},
143  {0x1.f7eb8d9ad84b9p+5, -0x1.5e28b7cc193ddp+2, -0x1.38bda7259441ep+4},
144  {0x1.0552717cdb82p+6, -0x1.1a33f75f67b0fp+4, 0x1.b0e8bd24c3fdep+3},
145  {0x1.08898a5b7805dp+6, -0x1.e29bd3071d97ap+3, 0x1.e0b5994e0238ep+2},
146  {0x1.0c10ad1b87866p+6, -0x1.a54f2215371a5p+3, 0x1.45758e5457898p+1},
147  {0x1.1111e924447e9p+6, -0x1.68a24e1d5efe1p+3, -0x1.7d178a838ea32p+1},
148  {0x1.178bb94d30a5cp+6, -0x1.335d7d75dd017p+3, -0x1.13f78c003ba13p+3},
149  {0x1.1f6aa49fcff2p+6, -0x1.0830d167759a4p+3, -0x1.c578e33f21d88p+3},
150  {0x1.30b236b57ac86p+6, -0x1.490afbe3d8e11p+4, 0x1.f8c35fbb689dap+3},
151  {0x1.331144aae0ad4p+6, -0x1.289bac3eeb83p+4, 0x1.626ea3a63748p+3},
152  {0x1.35b09f5c62a7ap+6, -0x1.0d33e3db59803p+4, 0x1.b66836d6f7ff6p+2},
153  {0x1.3973d5b39baa4p+6, -0x1.de87e61f0c86ap+3, 0x1.de8cf8346969cp+0},
154  {0x1.3e64dbeab2c38p+6, -0x1.a47dea5d85dbbp+3, -0x1.bc586cdcba45p+1},
155  {0x1.44815d7f73a41p+6, -0x1.706c35b4850ecp+3, -0x1.1d13e731dfc5bp+3},
156  {0x1.5a92b1ff8af32p+6, -0x1.76441609cfb3ap+4, 0x1.1f1186319beaap+4},
157  {0x1.5c6885a0ab4cap+6, -0x1.5c0bf4148d03dp+4, 0x1.c5b4a75d0a01ep+3},
158  {0x1.5e72281eb918cp+6, -0x1.4422b7d2ad44bp+4, 0x1.5305a49da3492p+3},
159  {0x1.61631d5752788p+6, -0x1.282b31110d79dp+4, 0x1.8b1a60b561753p+2},
160  {0x1.65488920f4795p+6, -0x1.0b1a0cfacacfbp+4, 0x1.3d72a3bb176fp+0},
161  {0x1.6a27499e4d3d6p+6, -0x1.de4892c062f8p+3, -0x1.f96a59bc9de4cp+1},
162  {0x1.e7d1475ebe201p+4, 0x1.5d4f5ebb6cf8ep+3, 0x1.86e150bac0e61p+2},
163  {0x1.10883ee613f6bp+5, 0x1.aa957aceb4328p+3, -0x1.06e4a6bcf37ep+3},
164  {0x1.2a507c82ee1e7p+5, 0x1.880d450b132c9p+3, -0x1.c81b5ba73664ap+3},
165  {0x1.4902475f20191p+5, 0x1.4f4dfeda4e013p+3, -0x1.38d6c960a7255p+4},
166  {0x1.6aa42ff68fb65p+5, 0x1.1116140836dp+3, -0x1.84f2dbb4bf7b2p+4},
167  {0x1.8def7d6adc3d5p+5, 0x1.ab6406f65a5b9p+2, -0x1.caee116cbc66ep+4},
168  {0x1.77f724f99d0c9p+5, -0x1.bb9ee0e906bf2p+1, 0x1.345d15707c9e4p+3},
169  {0x1.8465d178eda3bp+5, -0x1.021519c6d64p-11, -0x1.11ebea130b3p-8},
170  {0x1.917d476fba08dp+5, 0x1.84e9b4b9a4816p+0, -0x1.89910d2df7414p+2},
171  {0x1.a32d016052029p+5, 0x1.352fbc1045df3p+1, -0x1.847eed4ab1e03p+3},
172  {0x1.b8cfd7baeb72ap+5, 0x1.5d6575ced028ep+1, -0x1.1bff70d82f589p+4},
173  {0x1.d1a20bfd91dbap+5, 0x1.4de285fe39a4dp+1, -0x1.6f381fca63bcep+4},
174  {0x1.c99e943a2d79fp+5, -0x1.219ec15c5de22p+3, 0x1.78f2db7cd8205p+3},
175  {0x1.d20af5c832c06p+5, -0x1.86c31353f2968p+2, 0x1.15f4d958ffb6ep+2},
176  {0x1.db2d2b76b7db4p+5, -0x1.107aa185fa178p+2, -0x1.3a545f6bb32fep+0},
177  {0x1.e7ef429658179p+5, -0x1.58a57459e2113p+1, -0x1.c50012145074dp+2},
178  {0x1.f821bad822a9ep+5, -0x1.8ea06e2a9e1f6p+0, -0x1.9a5cf82ebb612p+3},
179  {0x1.05b4f2f9696abp+6, -0x1.b3645fe7f9294p-1, -0x1.24f471b4e8b62p+4},
180  {0x1.0e4a1f1b1ddcep+6, -0x1.b32bbdf7204c1p+3, 0x1.be3e8e0b04a1fp+3},
181  {0x1.114fcf04f35b3p+6, -0x1.6696d5115c7e4p+3, 0x1.058704ea2708p+3},
182  {0x1.14a2558b5b6c7p+6, -0x1.2c7e211429d81p+3, 0x1.a9f809f8ee4dfp+1},
183  {0x1.195c00cf22a51p+6, -0x1.e58de99c40fb6p+2, -0x1.0e4f6479339a5p+1},
184  {0x1.1f7e4eea78cb7p+6, -0x1.809e6530a25d5p+2, -0x1.ee2cfac349d5ep+2},
185  {0x1.26f9f5da0f33bp+6, -0x1.2ffe0404fe1e4p+2, -0x1.a87af760f19c2p+3},
186  {0x1.37635555b270cp+6, -0x1.17deced6d377ep+4, 0x1.0159387931185p+4},
187  {0x1.39aa8b444a1c7p+6, -0x1.f1b4258b74e35p+3, 0x1.70a1d9ed49333p+3},
188  {0x1.3c30003dc4bdap+6, -0x1.bcf061cd62971p+3, 0x1.d845b8f464806p+2},
189  {0x1.3fcf0a7e4fdb8p+6, -0x1.8316f8fb04f1cp+3, 0x1.3be7d797ab219p+1},
190  {0x1.4492530767fa4p+6, -0x1.4af657f86bc35p+3, -0x1.69ccecfbead6p+1},
191  {0x1.4a78e7a0304fbp+6, -0x1.18be182ded159p+3, -0x1.07ac09ccdfff4p+3},
192  {0x1.5fc9ac083946p+6, -0x1.4f840d59cd9aep+4, 0x1.22ef620ec7775p+4},
193  {0x1.6192b2ae205fp+6, -0x1.361d4509435d5p+4, 0x1.cfe893676e62ep+3},
194  {0x1.638e487d443e1p+6, -0x1.1edcf418396fcp+4, 0x1.5f105a3f7b33p+3},
195  {0x1.666b5164f3799p+6, -0x1.03988ade0159ep+4, 0x1.a6a2e5baac692p+2},
196  {0x1.6a3706fa48d42p+6, -0x1.ce6b98f424c54p+3, 0x1.b67fa4fafc2e8p+0},
197  {0x1.6ef6b7860b53fp+6, -0x1.97ce09961f218p+3, -0x1.b926ed0fbe897p+1},
198  {0x1.3931bb83cb32dp+5, 0x1.c0894426a198dp+3, 0x1.f5ea328bf4058p+2},
199  {0x1.4b9e77eb58ebfp+5, 0x1.108cfd41d7919p+4, -0x1.0eaf04c8d3b35p+2},
200  {0x1.5df2d7bacd40ap+5, 0x1.11e15f9b225acp+4, -0x1.51924b9c514f3p+3},
201  {0x1.756bc7d79519bp+5, 0x1.03a63c750b36bp+4, -0x1.052fc194cf52p+4},
202  {0x1.90b3e1d276c5dp+5, 0x1.d79fd75fb3811p+3, -0x1.58e6a7a1da1fcp+4},
203  {0x1.aea5654d2d631p+5, 0x1.9f14550c88c57p+3, -0x1.a57e86563dbcap+4},
204  {0x1.9b6948efcb1e8p+5, 0x1.dddaa142e7b3ap+0, 0x1.4f83ba256abcfp+3},
205  {0x1.a5f9bfdb796c8p+5, 0x1.3de3647070a5bp+2, 0x1.b416b1e2a3817p+0},
206  {0x1.b1407c7a80b46p+5, 0x1.9d8674f672f17p+2, -0x1.112d7835e0443p+2},
207  {0x1.c0b6552db89b1p+5, 0x1.d8c2a5262b398p+2, -0x1.480f10f6d0b5fp+3},
208  {0x1.d3edc2b5ca1f7p+5, 0x1.edbf49a0778e4p+2, -0x1.fe5ca96caae15p+3},
209  {0x1.ea519f7b4bf8cp+5, 0x1.e35c8bfe38bf9p+2, -0x1.5483ed4b7e5ebp+4},
210  {0x1.e2c36ca30962cp+5, -0x1.1cd1878e91233p+2, 0x1.8bf558e4fbde3p+3},
211  {0x1.ea67769abd5c1p+5, -0x1.c053e5b6c3b66p+0, 0x1.59302161a2865p+2},
212  {0x1.f2ba2dd022991p+5, -0x1.4b64e809e68p-11, -0x1.5fbb8b338b8p-8},
213  {0x1.fe6b844bbf20fp+5, 0x1.8040f07e39554p+0, -0x1.7196ffd7de002p+2},
214  {0x1.06aeef460087dp+6, 0x1.4afa79e390244p+1, -0x1.705a7c8493135p+3},
215  {0x1.0fa5554a5c955p+6, 0x1.9dea7336ee828p+1, -0x1.1089bb9ca046ep+4},
216  {0x1.178e9051b853ep+6, -0x1.3b3b8e6c52f1cp+3, 0x1.cc268b9a4e157p+3},
217  {0x1.1a65db5c4c0d1p+6, -0x1.e697a167b2b86p+2, 0x1.1b4d0d1ca381dp+3},
218  {0x1.1d86bd983524p+6, -0x1.77ccb1bcef226p+2, 0x1.08e1384d16e75p+2},
219  {0x1.21fc7ad985402p+6, -0x1.08e3f099fb00fp+2, -0x1.363e6fbac8902p+0},
220  {0x1.27cb1d6dd9434p+6, -0x1.4f7ef424b1025p+1, -0x1.b1af4c945ce98p+2},
221  {0x1.2ee710ec206cfp+6, -0x1.6ac7f2787bda1p+0, -0x1.89e8c18db3849p+3},
222  {0x1.3e7c86695f695p+6, -0x1.ceac064fd3a97p+3, 0x1.06a67a74ff4cp+4},
223  {0x1.40ac0504d65f2p+6, -0x1.935bda5136fdbp+3, 0x1.7fa8ea5683dffp+3},
224  {0x1.4317a9d652f14p+6, -0x1.608608dcc2607p+3, 0x1.fc21edd271f8bp+2},
225  {0x1.46928cf49114ap+6, -0x1.2883a0410f74bp+3, 0x1.8d4b911a8102bp+1},
226  {0x1.4b27f53f2d1c8p+6, -0x1.e40f8922e26b2p+2, -0x1.11ca0bf0acee9p+1},
227  {0x1.50d83b6f7788bp+6, -0x1.82a0417f98419p+2, -0x1.e18432a6676edp+2},
228  {0x1.6567db05a012cp+6, -0x1.27d55f8b64f16p+4, 0x1.271e9400a42e4p+4},
229  {0x1.6723b3feab73ap+6, -0x1.0f412c5a09d1ap+4, 0x1.dae43f7f7eb6ep+3},
230  {0x1.6910cf423acf1p+6, -0x1.f152d3d18c3b3p+3, 0x1.6c04c44876eb2p+3},
231  {0x1.6bd948eca017fp+6, -0x1.bc29d2c53f57ap+3, 0x1.c44a28d107a62p+2},
232  {0x1.6f8a69cf10b1p+6, -0x1.84b224c6d040cp+3, 0x1.1c910c629db67p+1},
233  {0x1.7429ebc4e406dp+6, -0x1.4f4a0b735c531p+3, -0x1.73a39097eadefp+1},
234  {0x1.7acf7694f8c6p+5, 0x1.0f40ef4bed7e8p+4, 0x1.2f88d81b23f2ep+3},
235  {0x1.87b52573912c8p+5, 0x1.3e9cadbae2c71p+4, -0x1.21c127942ffc2p-1},
236  {0x1.95308092c646cp+5, 0x1.4bd8ec93cbe28p+4, -0x1.b1ad2fc6cd5ccp+2},
237  {0x1.a743d71712a69p+5, 0x1.4b428f09e4704p+4, -0x1.984b766ca527bp+3},
238  {0x1.bd35da1ada54ap+5, 0x1.3ee7b023f1a36p+4, -0x1.252f399063a3p+4},
239  {0x1.d638af110e2bfp+5, 0x1.2a6369728426p+4, -0x1.777a4238ecc9cp+4},
240  {0x1.c5db1e678f93bp+5, 0x1.be23ef0d11b9fp+2, 0x1.706e52e6555aep+3},
241  {0x1.ceab15b1e5e2cp+5, 0x1.376364145a451p+3, 0x1.d7fd2a954eedcp+1},
242  {0x1.d8302a174d63dp+5, 0x1.67a05469bf7c1p+3, -0x1.0236bbe6ffddap+1},
243  {0x1.e56c6a282c6fep+5, 0x1.8915eed9a5994p+3, -0x1.fb2d49a257381p+2},
244  {0x1.f622a3ef65e7fp+5, 0x1.98638aed6156bp+3, -0x1.b57e66e36241ap+3},
245  {0x1.04f5c4d9c25cfp+6, 0x1.9710ff57a7b7ap+3, -0x1.32004fe7bd5b5p+4},
246  {0x1.018127d59826cp+6, 0x1.1e619caaa3a8fp-1, 0x1.a49bd0215a96dp+3},
247  {0x1.04e57659b4cf6p+6, 0x1.8024718692a3cp+1, 0x1.addd1b874609p+2},
248  {0x1.089bd543a0e63p+6, 0x1.2a7652607e02cp+2, 0x1.8d85013677bap+0},
249  {0x1.0ddb424d04fadp+6, 0x1.889c9b8223651p+2, -0x1.05aa1bc86a9d4p+2},
250  {0x1.149da8fbd8909p+6, 0x1.ce4542c130c31p+2, -0x1.3934aac98d184p+3},
251  {0x1.1cca409e32c6dp+6, 0x1.f855300b25ba5p+2, -0x1.eac45e49f86bap+3},
252  {0x1.23f3e081f0e6fp+6, -0x1.587d5d644e818p+2, 0x1.deea03a7e9e2p+3},
253  {0x1.26935d2d5bb58p+6, -0x1.a6ab45ebb79ddp+1, 0x1.38265c4c6d156p+3},
254  {0x1.29783d4927cb6p+6, -0x1.aa081e388f0b6p+0, 0x1.4de28afb99798p+2},
255  {0x1.2d9b5abdd0df6p+6, -0x1.90d2c34c4c8p-11, -0x1.a96c3913736p-8},
256  {0x1.3303704c60227p+6, 0x1.7842d686da2p+0, -0x1.6001098473213p+2},
257  {0x1.39a911d79bf6cp+6, 0x1.51ae39272f6fp+1, -0x1.604eff9e13224p+3},
258  {0x1.483b66bb75f41p+6, -0x1.53e4a65e5bd0ep+3, 0x1.0dfa4f4feec89p+4},
259  {0x1.4a4cab5bdb777p+6, -0x1.1bf0c7950b46ap+3, 0x1.9433415e2e559p+3},
260  {0x1.4c9756fc325a6p+6, -0x1.d6e499fe26166p+2, 0x1.1693b52228314p+3},
261  {0x1.4fe3e8502420ap+6, -0x1.6b29463056a7ep+2, 0x1.fcea504279e1bp+1},
262  {0x1.543e4f84c51c9p+6, -0x1.01c201c3fcfbep+2, -0x1.311b349896504p+0},
263  {0x1.59a86b698fe17p+6, -0x1.4697ac30fd276p+1, -0x1.a218614573a54p+2},
264  {0x1.6d3f94c53849cp+6, -0x1.e750c3b6abf94p+3, 0x1.2cfd8e3a3298cp+4},
265  {0x1.6ee9ffc7c561bp+6, -0x1.b847cb4caca9ap+3, 0x1.ea32a397a5ea3p+3},
266  {0x1.70c3ef56a2364p+6, -0x1.8cc812a446223p+3, 0x1.7e0dc7e1aa4eep+3},
267  {0x1.737125604c02dp+6, -0x1.5959ff3792892p+3, 0x1.ed9e6884d6db8p+2},
268  {0x1.76fef3ccb11e4p+6, -0x1.2379a952d29eap+3, 0x1.77d044e094287p+1},
269  {0x1.7b739668b58adp+6, -0x1.def62d62ac5d3p+2, -0x1.1242c0559f9b9p+1},
270  {0x1.b9af6705b3e1ap+5, 0x1.3c46b4e4aa724p+4, 0x1.61ea416f62116p+3},
271  {0x1.c34652a386648p+5, 0x1.673d5400dac6ap+4, 0x1.575529f864e4ap+1},
272  {0x1.cd90a5a7155efp+5, 0x1.7aac661c86347p+4, -0x1.97f5d2781d1d2p+1},
273  {0x1.dbc2183aadc6dp+5, 0x1.83f34257f81ecp+4, -0x1.24c7f27451716p+3},
274  {0x1.ed84b050fd5fep+5, 0x1.823e0b9a9286fp+4, -0x1.dc10370a896a1p+3},
275  {0x1.012d3f7585d8bp+6, 0x1.772916c92c8cp+4, -0x1.444d46ad4a3f5p+4},
276  {0x1.f498a57d50c8dp+5, 0x1.72c50647a1b01p+3, 0x1.9501b7bc92a26p+3},
277  {0x1.fbec5add8799fp+5, 0x1.c10937ac1ff0cp+3, 0x1.71506b03a0cfcp+2},
278  {0x1.01f4d75996fc6p+6, 0x1.f168c838b27a5p+3, 0x1.a2871b4170f08p-2},
279  {0x1.07934868d42f1p+6, 0x1.0ba82dcee8d5fp+4, -0x1.55f50c12fdb69p+2},
280  {0x1.0ec4c024626fep+6, 0x1.16b4701bb2cep+4, -0x1.627c9a1b01177p+3},
281  {0x1.1768d8b0aa031p+6, 0x1.199f4e2e9e65fp+4, -0x1.09c1c4e924702p+4},
282  {0x1.14522a4a9a7e2p+6, 0x1.6020b0a7ecac7p+2, 0x1.c1a8f99df62d9p+3},
283  {0x1.174b930ef061ap+6, 0x1.ec83068a9839fp+2, 0x1.06f134f7cc07ep+3},
284  {0x1.1a90921a38fe7p+6, 0x1.28d89eb448a2cp+3, 0x1.ab5fe99a94265p+1},
285  {0x1.1f36b9520ad75p+6, 0x1.57ace8397fb96p+3, -0x1.0feb2da7758d4p+1},
286  {0x1.253f2b741784fp+6, 0x1.7c087d08384b1p+3, -0x1.efdb49212f992p+2},
287  {0x1.2c9a998952cf1p+6, 0x1.9366b5a4edd8ap+3, -0x1.a9848b6099745p+3},
288  {0x1.330541ae3e385p+6, -0x1.39e238ea7801p-1, 0x1.f5ec9f058d778p+3},
289  {0x1.3568ff8940412p+6, 0x1.4b2aea6a2b17bp+0, 0x1.5aadb9534e15p+3},
290  {0x1.380d495b09ffp+6, 0x1.6b2260c162acep+1, 0x1.a0b3e40fdedfp+2},
291  {0x1.3bd6db55a86abp+6, 0x1.1c0894632c07ep+2, 0x1.73acfbc73a52cp+0},
292  {0x1.40ced5740b6ecp+6, 0x1.784d8d5bd59bap+2, -0x1.f87f29ae0432ap+1},
293  {0x1.46f1d56d6a627p+6, 0x1.c2acec311d85p+2, -0x1.2d078d1d0ec93p+3},
294  {0x1.54692a261ff77p+6, -0x1.91aafde5841e2p+2, 0x1.1733b7c5acf09p+4},
295  {0x1.5657fb0185f53p+6, -0x1.292fef1c6094ap+2, 0x1.adacb9827ba1ep+3},
296  {0x1.587cf8c168362p+6, -0x1.9aa045411de21p+1, 0x1.34f47fa15abacp+3},
297  {0x1.5b946ca219efdp+6, -0x1.98465c0dc7b2ap+0, 0x1.43ded9d06d2f2p+2},
298  {0x1.5faadb5249c01p+6, -0x1.d35a0bdd6ap-11, -0x1.f008c176226p-8},
299  {0x1.64c3b31613c93p+6, 0x1.6ffd305ac700dp+0, -0x1.52580b551209ep+2},
300  {0x1.773c645e32d2dp+6, -0x1.6c001f7fba478p+3, 0x1.3482f283026fp+4},
301  {0x1.78d215b19332bp+6, -0x1.3f6c1cce8d703p+3, 0x1.fda06ae8dbf53p+3},
302  {0x1.7a9531dc8addap+6, -0x1.15dd42d16c7e4p+3, 0x1.94e8098e96fccp+3},
303  {0x1.7d21e2193d5bcp+6, -0x1.c8c718caa9f8ep+2, 0x1.1105d830d79dp+3},
304  {0x1.808572bc9578ap+6, -0x1.607bf910c57eep+2, 0x1.ebe2b93545493p+1},
305  {0x1.84c6a7914070fp+6, -0x1.f6c2ba3dfc25ap+1, -0x1.2beab2de5e36ep+0},
306  {0x1.f663baac570efp+5, 0x1.67be9f690c994p+4, 0x1.928e76c3750aep+3},
307  {0x1.fdd78bfa0c16bp+5, 0x1.8da11d29326eap+4, 0x1.63128e5e5d16fp+2},
308  {0x1.02fa8211ca545p+6, 0x1.a3a4c44447321p+4, 0x1.04d0a318f0ebp-3},
309  {0x1.08ace6a6b1bbcp+6, 0x1.b32f97fdc6c72p+4, -0x1.69ea7f28a916ap+2},
310  {0x1.0ff3cb8943e81p+6, 0x1.b9bf03db71bbbp+4, -0x1.6cb54bc7e9c6p+3},
311  {0x1.18ac5c68852cdp+6, 0x1.b753a9bd5dbeep+4, -0x1.0ebf0dff35bfbp+4},
312  {0x1.12e57190e4906p+6, 0x1.f7243f8456bc9p+3, 0x1.bbbf019d6f15fp+3},
313  {0x1.15f567ae01685p+6, 0x1.1e238872ebfe1p+4, 0x1.f5db81101607bp+2},
314  {0x1.1952176f81196p+6, 0x1.35eb43e1b3454p+4, 0x1.6f044118da651p+1},
315  {0x1.1e17b3f5be1e9p+6, 0x1.4aa233635e996p+4, -0x1.54536023af51fp+1},
316  {0x1.244540fde90c8p+6, 0x1.590428a4cd957p+4, -0x1.09fd595c81312p+3},
317  {0x1.2bc8d270727cdp+6, 0x1.6002d76c0fae1p+4, -0x1.bbbb8dd0769e1p+3},
318  {0x1.2910051d7f4b8p+6, 0x1.4505f3d8c93bep+3, 0x1.e1f0441f64aep+3},
319  {0x1.2ba848891bba1p+6, 0x1.83d80a13be75dp+3, 0x1.39f759ee811ebp+3},
320  {0x1.2e852380cafc2p+6, 0x1.b3d439de20201p+3, 0x1.5018eb1694a48p+2},
321  {0x1.329c951f395aap+6, 0x1.e28db45c54996p+3, 0x1.689e197414bp-7},
322  {0x1.37f5013da44b7p+6, 0x1.04846845ce376p+4, -0x1.5f9623aa14c25p+2},
323  {0x1.3e86c9cea44c3p+6, 0x1.11f59319394fdp+4, -0x1.6049926fe33dap+3},
324  {0x1.443efb939f1d1p+6, 0x1.0a131d35fab88p+2, 0x1.0838884c6c35ap+4},
325  {0x1.4667729b66dbap+6, 0x1.793a281884d1p+2, 0x1.8155ad5254ba1p+3},
326  {0x1.48cb51f255527p+6, 0x1.d609d14c020d1p+2, 0x1.fd82e0735fdcep+2},
327  {0x1.4c3af36c4bb13p+6, 0x1.1c709781a9fb2p+3, 0x1.8ca66dc4e33fdp+1},
328  {0x1.50c14ed0ba88bp+6, 0x1.4a1d653379591p+3, -0x1.14ee52d3fd0e1p+1},
329  {0x1.565e87fd89a8p+6, 0x1.6fe5c4c37980fp+3, -0x1.e3e3e3d81f648p+2},
330  {0x1.62b687ada6a2dp+6, -0x1.b1a7c98c53b9bp+0, 0x1.221d2fc2be6cp+4},
331  {0x1.6480f342fedfap+6, -0x1.728e49c5a9ba8p-3, 0x1.cb44060a17de8p+3},
332  {0x1.667e081a8e931p+6, 0x1.2c204353d8acp+0, 0x1.582d07a1ac706p+3},
333  {0x1.695d03a4b4ea6p+6, 0x1.5b9dd1e5192c9p+1, 0x1.949cc8da7db3dp+2},
334  {0x1.6d2ad25b49139p+6, 0x1.10c661536b1e3p+2, 0x1.60b9aee667ea2p+0},
335  {0x1.71ec510363cccp+6, 0x1.6b2af260ca3f1p+2, -0x1.e8e3d6769ed61p+1},
336  {0x1.83323c984ee7ap+6, -0x1.c8df58716d4cbp+2, 0x1.3d9335b5d20f9p+4},
337  {0x1.84b109344c92cp+6, -0x1.750134f94569cp+2, 0x1.0a63166d3ca32p+4},
338  {0x1.865ae5967696dp+6, -0x1.260f129da839ep+2, 0x1.b00f6d0ac1f93p+3},
339  {0x1.88c3891dbffdp+6, -0x1.8e7bf5d542ea4p+1, 0x1.30323669c3d67p+3},
340  {0x1.8bf825267af8ep+6, -0x1.89d8c0f2ba1ffp+0, 0x1.3b2af0c9eed76p+2},
341  {0x1.8ffffbb77c76ap+6, -0x1.09cab717214p-10, -0x1.1a1aa7765a7p-7},
342  {0x1.ae1c063cf8075p+3, -0x1.1dcc8d6b21p-13, -0x1.2f56d49352fp-10},
343  {0x1.23869fde6955fp+4, -0x1.836d13c82dp-13, -0x1.9b340cb2926p-10},
344  {0x1.6a51d9755cb1ep+4, -0x1.e1821bf6d08p-13, -0x1.ff0f3c5806ap-10},
345  {0x1.adca073d0c9a1p+4, -0x1.1d961152df8p-12, -0x1.2f1d00745cap-9},
346  {0x1.eeb26a3638306p+4, -0x1.48b751f0a58p-12, -0x1.5ce3e1a3db8p-9},
347  {0x1.16c4868a9dbc4p+5, -0x1.7278906708p-12, -0x1.89352628678p-9},
348  {0x1.3552cb4726ed2p+5, -0x1.9b140ac6fbp-12, -0x1.b44e9f2325p-9},
349  {0x1.53242132979b2p+5, -0x1.c2b46f9c328p-12, -0x1.de5d99b6f9p-9},
350  {0x1.7051013cf5a69p+5, -0x1.e97a44c10a8p-12, -0x1.03c24d5b897p-8},
351  {0x1.8ceca3a0569fdp+5, -0x1.07bf8a7e0fcp-11, -0x1.17ef5f1c441p-8},
352  {0x1.a9067ed63306p+5, -0x1.1a6bb67a6bp-11, -0x1.2bc0e9df0a8p-8},
353  {0x1.c4ab42f91535bp+5, -0x1.2cca14a233p-11, -0x1.3f3fe0662d2p-8},
354  {0x1.dfe5855ae1528p+5, -0x1.3ee1ad40618p-11, -0x1.5273b9784a3p-8},
355  {0x1.fabe397d15c9dp+5, -0x1.50b872fc158p-11, -0x1.6562c531248p-8},
356  {0x1.0a9e8459f8d9ap+6, -0x1.62537d1958p-11, -0x1.78126ad030ep-8},
357  {0x1.17b44990f13f5p+6, -0x1.73b732a42fp-11, -0x1.8a87568ccdp-8},
358  {0x1.24a350705ddf4p+6, -0x1.84e76b1c308p-11, -0x1.9cc59c424aap-8},
359  {0x1.316e23ed9d96ap+6, -0x1.95e7879947p-11, -0x1.aed0d2216dp-8},
360  {0x1.3e1704b29c069p+6, -0x1.a6ba8679e08p-11, -0x1.c0ac258f992p-8},
361  {0x1.4a9ff4d8984e6p+6, -0x1.b76312f3808p-11, -0x1.d25a6bb27acp-8},
362  {0x1.570ac151c1615p+6, -0x1.c7e3919c138p-11, -0x1.e3de2eb684p-8},
363  {0x1.6359098c40c61p+6, -0x1.d83e2a89bbp-11, -0x1.f539b894d66p-8},
364  {0x1.6f8c45b456692p+6, -0x1.e874d1a7f5p-11, -0x1.03378df3804p-7},
365  {0x1.7ba5cbe12c3fcp+6, -0x1.f8894d9602p-11, -0x1.0bc01d99c93p-7},
366  };
367 
368  // Perform the inverse gamma companding for a sRGB color
369  // http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
370  // double inverseGammaCompanding(int c) {
371  // // [0, 255] to [0, 1]
372  // double v = c / 255.0;
373  // if (v <= 0.04045) {
374  // return v / 12.92;
375  // }
376  // return std::pow((v + 0.055) / 1.055, 2.4);
377  // };
378  constexpr double RGB888_to_sRGB_table[] {
379  0x0p+0, 0x1.3e45677c176f7p-12, 0x1.3e45677c176f7p-11, 0x1.dd681b3a23272p-11,
380  0x1.3e45677c176f7p-10, 0x1.8dd6c15b1d4b4p-10, 0x1.dd681b3a23272p-10, 0x1.167cba8c94818p-9,
381  0x1.3e45677c176f7p-9, 0x1.660e146b9a5d6p-9, 0x1.8dd6c15b1d4b4p-9, 0x1.b6a31b5259c99p-9,
382  0x1.e1e31d70c99ddp-9, 0x1.07c38bf8583a9p-8, 0x1.1fcc2beed6421p-8, 0x1.390ffaf95e279p-8,
383  0x1.53936cc7bc928p-8, 0x1.6f5addb50c915p-8, 0x1.8c6a94031b561p-8, 0x1.aac6c0fb97351p-8,
384  0x1.ca7381f9f602bp-8, 0x1.eb74e160978dp-8, 0x1.06e76bbda92b8p-7, 0x1.18c2a5a8a8044p-7,
385  0x1.2b4e09b3f0ae3p-7, 0x1.3e8b7b3bde965p-7, 0x1.527cd60af8b85p-7, 0x1.6723eea8d3709p-7,
386  0x1.7c8292a3db6b3p-7, 0x1.929a88d67b521p-7, 0x1.a96d91a8016bdp-7, 0x1.c0fd67499fab6p-7,
387  0x1.d94bbdefd740ep-7, 0x1.f25a44089883fp-7, 0x1.061551372c694p-6, 0x1.135f3e4c2cce2p-6,
388  0x1.210bb8642b172p-6, 0x1.2f1b8c1ae46bdp-6, 0x1.3d8f839b79c0bp-6, 0x1.4c6866b3e9fa4p-6,
389  0x1.5ba6fae794313p-6, 0x1.6b4c0380d2deep-6, 0x1.7b5841a1bf3acp-6, 0x1.8bcc74542addbp-6,
390  0x1.9ca95898dc8b5p-6, 0x1.adefa9761c02p-6, 0x1.bfa0200597bd9p-6, 0x1.d1bb7381aec1fp-6,
391  0x1.e442595227bcap-6, 0x1.f73585185e1b5p-6, 0x1.054ad45d76878p-5, 0x1.0f31ba386ff26p-5,
392  0x1.194fcb663747bp-5, 0x1.23a55e62a662ap-5, 0x1.2e32c8e148d11p-5, 0x1.38f85fd21eacfp-5,
393  0x1.43f67766310ffp-5, 0x1.4f2d6313fa8dp-5, 0x1.5a9d759ba5edp-5, 0x1.6647010b254eep-5,
394  0x1.722a56c2239eep-5, 0x1.7e47c775d2427p-5, 0x1.8a9fa33494b07p-5, 0x1.973239698b9ccp-5,
395  0x1.a3ffd8e001389p-5, 0x1.b108cfc6b7fbcp-5, 0x1.be4d6bb31d522p-5, 0x1.cbcdf9a4616f2p-5,
396  0x1.d98ac60675833p-5, 0x1.e7841cb4f16dfp-5, 0x1.f5ba48fde2048p-5, 0x1.0216cad240765p-4,
397  0x1.096f2671eb815p-4, 0x1.10e65c38a5192p-4, 0x1.187c90bf8bce2p-4, 0x1.2031e85f5d6dap-4,
398  0x1.28068731a1952p-4, 0x1.2ffa9111cb94bp-4, 0x1.380e299e53f92p-4, 0x1.40417439ca10fp-4,
399  0x1.4894940bddbfbp-4, 0x1.5107ac0261e59p-4, 0x1.599aded247aacp-4, 0x1.624e4ef892ed4p-4,
400  0x1.6b221ebb4817ep-4, 0x1.7416702a539d1p-4, 0x1.7d2b65206b527p-4, 0x1.86611f43e9e6ap-4,
401  0x1.8fb7c007a4a7p-4, 0x1.992f68abbbc89p-4, 0x1.a2c83a3e6566dp-4, 0x1.ac82559cb3644p-4,
402  0x1.b65ddb7354604p-4, 0x1.c05aec3f4fe5ep-4, 0x1.ca79a84ebe03p-4, 0x1.d4ba2fc17a6a5p-4,
403  0x1.df1ca289d34b8p-4, 0x1.e9a1206d34003p-4, 0x1.f447c904cbb4ep-4, 0x1.ff10bbbe302c2p-4,
404  0x1.04fe0bedfe5f1p-3, 0x1.0a84fe3b36d8fp-3, 0x1.101d443dfc06fp-3, 0x1.15c6ed58eefdfp-3,
405  0x1.1b8208da5fefp-3, 0x1.214ea5fc9514ap-3, 0x1.272cd3e610123p-3, 0x1.2d1ca1a9d1cfbp-3,
406  0x1.331e1e479cdf5p-3, 0x1.393158ac3674ep-3, 0x1.3f565fb1a5fd5p-3, 0x1.458d421f735dfp-3,
407  0x1.4bd60eaae3e73p-3, 0x1.5230d3f736034p-3, 0x1.589da095dbaa1p-3, 0x1.5f1c8306b3a3cp-3,
408  0x1.65ad89b841a2bp-3, 0x1.6c50c307e53bfp-3, 0x1.73063d420fc8p-3, 0x1.79ce06a279303p-3,
409  0x1.80a82d5453b5dp-3, 0x1.8794bf727eb3fp-3, 0x1.8e93cb07b8679p-3, 0x1.95a55e0ecec0bp-3,
410  0x1.9cc98672cf47ep-3, 0x1.a400520f3619cp-3, 0x1.ab49ceb01c003p-3, 0x1.b2a60a1263b0ap-3,
411  0x1.ba1511e3e632dp-3, 0x1.c196f3c39e76fp-3, 0x1.c92bbd41d41fep-3, 0x1.d0d37be045851p-3,
412  0x1.d88e3d1250f68p-3, 0x1.e05c0e3d1d3ep-3, 0x1.e83cfcb7c16fp-3, 0x1.f03115cb6bfd3p-3,
413  0x1.f83866b38924dp-3, 0x1.00297e4ef4553p-2, 0x1.044072557177ap-2, 0x1.086115f6beb3ap-2,
414  0x1.0c8b6fb5c735ep-2, 0x1.10bf860ef039ap-2, 0x1.14fd5f782a5a6p-2, 0x1.1945026102997p-2,
415  0x1.1d967532b31b1p-2, 0x1.21f1be50339e7p-2, 0x1.2656e41649ae3p-2, 0x1.2ac5ecdb988f8p-2,
416  0x1.2f3edef0b0ed8p-2, 0x1.33c1c0a020438p-2, 0x1.384e982e800b1p-2, 0x1.3ce56bda84a81p-2,
417  0x1.418641dd0c1bcp-2, 0x1.463120692c7afp-2, 0x1.4ae60dac4229dp-2, 0x1.4fa50fcdfde15p-2,
418  0x1.546e2cf0727a9p-2, 0x1.59416b3022858p-2, 0x1.5e1ed0a40daabp-2, 0x1.6306635dbdd7bp-2,
419  0x1.67f82969543a2p-2, 0x1.6cf428cd96079p-2, 0x1.71fa678bf915dp-2, 0x1.770aeba0b042ap-2,
420  0x1.7c25bb02b7ac5p-2, 0x1.814adba3e0bd9p-2, 0x1.867a5370de0b1p-2, 0x1.8bb428514f067p-2,
421  0x1.90f86027cb84ep-2, 0x1.964700d1ef1b1p-2, 0x1.9ba0102864521p-2, 0x1.a10393feefafdp-2,
422  0x1.a67192247a9bep-2, 0x1.abea10631e195p-2, 0x1.b16d14802d5cap-2, 0x1.b6faa43c403bbp-2,
423  0x1.bc92c5533d785p-2, 0x1.c2357d7c64e5dp-2, 0x1.c7e2d26a596dep-2, 0x1.cd9ac9cb2aef2p-2,
424  0x1.d35d69485ffc5p-2, 0x1.d92ab686ff782p-2, 0x1.df02b7279a10dp-2, 0x1.e4e570c6539c5p-2,
425  0x1.ead2e8faec526p-2, 0x1.f0cb2558c9ea4p-2, 0x1.f6ce2b6f00983p-2, 0x1.fcdc00c85bec2p-2,
426  0x1.017a5575b3cb2p-1, 0x1.048c17ad3c04bp-1, 0x1.07a349c9d9837p-1, 0x1.0abfee888c05p-1,
427  0x1.0de208a4444c8p-1, 0x1.11099ad5e83ebp-1, 0x1.1436a7d456eefp-1, 0x1.176932546ca12p-1,
428  0x1.1aa13d0906bdap-1, 0x1.1ddecaa307b85p-1, 0x1.2121ddd15aecep-1, 0x1.246a7940f86d1p-1,
429  0x1.27b89f9ce8c4bp-1, 0x1.2b0c538e48b07p-1, 0x1.2e6597bc4ccap-1, 0x1.31c46ecc4528dp-1,
430  0x1.3528db61a0f73p-1, 0x1.3892e01df1fccp-1, 0x1.3c027fa0f01ebp-1, 0x1.3f77bc887cd3bp-1,
431  0x1.42f29970a68f8p-1, 0x1.467318f3ac22dp-1, 0x1.49f93daa00113p-1, 0x1.4d850a2a4bde1p-1,
432  0x1.51168109734e5p-1, 0x1.54ada4da97a1bp-1, 0x1.584a782f1ac23p-1, 0x1.5becfd96a2698p-1,
433  0x1.5f95379f1b3edp-1, 0x1.634328d4bbe97p-1, 0x1.66f6d3c2081cfp-1, 0x1.6ab03aefd39aap-1,
434  0x1.6e6f60e5452b1p-1, 0x1.72344827d98f6p-1, 0x1.75fef33b6669bp-1, 0x1.79cf64a21d1e2p-1,
435  0x1.7da59edc8dabp-1, 0x1.8181a469a9787p-1, 0x1.856377c6c6224p-1, 0x1.894b1b6fa0377p-1,
436  0x1.8d3891de5df49p-1, 0x1.912bdd8b91f45p-1, 0x1.952500ee3dda5p-1, 0x1.9923fe7bd4f67p-1,
437  0x1.9d28d8a83edfcp-1, 0x1.a13391e5da09fp-1, 0x1.a5442ca57e52ep-1, 0x1.a95aab567f88fp-1,
438  0x1.ad771066afec2p-1, 0x1.b1995e4262a69p-1, 0x1.b5c197546e3f8p-1, 0x1.b9efbe062f086p-1,
439  0x1.be23d4bf8981bp-1, 0x1.c25ddde6ecbbbp-1, 0x1.c69ddbe154af1p-1, 0x1.cae3d1124c90bp-1,
440  0x1.cf2fbfdbf11f1p-1, 0x1.d381aa9ef2e82p-1, 0x1.d7d993ba988d4p-1, 0x1.dc377d8cc0fd5p-1,
441  0x1.e09b6a71e5aa6p-1, 0x1.e5055cc51cbb4p-1, 0x1.e97556e01b351p-1, 0x1.edeb5b1b37216p-1,
442  0x1.f2676bcd69adep-1, 0x1.f6e98b4c51466p-1, 0x1.fb71bbec33ab2p-1, 0x1p+0,
443  };
444 // clang-format on
445 
446 // convert a RGB color to Oklab (https://bottosson.github.io/posts/oklab/)
447 Lab rgbToOklab(QRgb rgb)
448 {
449  const double r = RGB888_to_sRGB_table[qRed(rgb)];
450  const double g = RGB888_to_sRGB_table[qGreen(rgb)];
451  const double b = RGB888_to_sRGB_table[qBlue(rgb)];
452 
453  const double l = std::cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
454  const double m = std::cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
455  const double s = std::cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
456 
457  // M2 * 100 * {l', m', s'}
458  return Lab{
459  (021.04542553 * l + 079.36177850 * m - 000.40720468 * s),
460  (197.79984951 * l - 242.85922050 * m + 045.05937099 * s),
461  (002.59040371 * l + 078.27717662 * m - 080.86757660 * s),
462  };
463 }
464 
465 constexpr double epsilon = 1e-15;
466 
467 inline double sinDegree(double x)
468 {
469  return std::sin(x * M_PI / 180.0);
470 }
471 
472 inline double cosDegree(double x)
473 {
474  return std::cos(x * M_PI / 180.0);
475 }
476 
477 inline double pow2(double x)
478 {
479  return x * x;
480 }
481 
482 inline double computeHPrime(double a_prime, double b)
483 {
484  if (std::abs(a_prime) < epsilon && std::abs(b) < epsilon) {
485  return 0.0;
486  }
487 
488  const double value = std::atan2(b, a_prime) * 180.0 / M_PI;
489  return (value < 0.0) ? value + 360.0 : value;
490 }
491 
492 inline double computeDeltaHPrime(double C1_prime, double C2_prime, double h1_prime, double h2_prime)
493 {
494  if (C1_prime * C2_prime < epsilon) {
495  return 0.0;
496  }
497 
498  const double diff = h2_prime - h1_prime;
499 
500  if (std::abs(diff) <= 180.0) {
501  return diff;
502  } else if (diff > 180.0) {
503  return diff - 360.0;
504  } else {
505  return diff + 360.0;
506  }
507 }
508 
509 inline double computeHPrimeBar(double C1_prime, double C2_prime, double h1_prime, double h2_prime)
510 {
511  const double sum = h1_prime + h2_prime;
512 
513  if (C1_prime * C2_prime < epsilon) {
514  return sum;
515  }
516 
517  const double dist = std::abs(h1_prime - h2_prime);
518 
519  if (dist <= 180.0) {
520  return 0.5 * sum;
521  } else if (sum < 360.0) {
522  return 0.5 * (sum + 360.0);
523  } else {
524  return 0.5 * (sum - 360.0);
525  }
526 }
527 
528 /// Calculate the perceptual color difference based on CIEDE2000.
529 /// https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
530 /// \return The color difference of the two colors.
531 double calculate_CIEDE2000(const Lab &color1, const Lab &color2)
532 {
533  const double L1 = color1.L;
534  const double a1 = color1.a;
535  const double b1 = color1.b;
536  const double L2 = color2.L;
537  const double a2 = color2.a;
538  const double b2 = color2.b;
539 
540  const double _25_pow_7 = /*std::pow(25.0, 7.0) = */ 6103515625.0;
541 
542  const double C1_ab = std::sqrt(a1 * a1 + b1 * b1);
543  const double C2_ab = std::sqrt(a2 * a2 + b2 * b2);
544  const double C_ab_bar = 0.5 * (C1_ab + C2_ab);
545  const double c_ab_bar_pow_7 = std::pow(C_ab_bar, 7.0);
546  const double G = 0.5 * (1.0 - std::sqrt(c_ab_bar_pow_7 / (c_ab_bar_pow_7 + _25_pow_7)));
547  const double a1_prime = (1.0 + G) * a1;
548  const double a2_prime = (1.0 + G) * a2;
549  const double C1_prime = std::sqrt(a1_prime * a1_prime + b1 * b1);
550  const double C2_prime = std::sqrt(a2_prime * a2_prime + b2 * b2);
551  const double h1_prime = computeHPrime(a1_prime, b1);
552  const double h2_prime = computeHPrime(a2_prime, b2);
553 
554  const double deltaL_prime = L2 - L1;
555  const double deltaC_prime = C2_prime - C1_prime;
556  const double deltah_prime = computeDeltaHPrime(C1_prime, C2_prime, h1_prime, h2_prime);
557  const double deltaH_prime = 2.0 * std::sqrt(C1_prime * C2_prime) * sinDegree(0.5 * deltah_prime);
558 
559  const double L_primeBar = 0.5 * (L1 + L2);
560  const double C_primeBar = 0.5 * (C1_prime + C2_prime);
561  const double h_primeBar = computeHPrimeBar(C1_prime, C2_prime, h1_prime, h2_prime);
562 
563  const double T = 1.0 - 0.17 * cosDegree(h_primeBar - 30.0) + 0.24 * cosDegree(2.0 * h_primeBar) + 0.32 * cosDegree(3.0 * h_primeBar + 6.0)
564  - 0.20 * cosDegree(4.0 * h_primeBar - 63.0);
565 
566  const double C_primeBar_pow7 = std::pow(C_primeBar, 7.0);
567  const double R_C = 2.0 * std::sqrt(C_primeBar_pow7 / (C_primeBar_pow7 + _25_pow_7));
568  const double S_L = 1.0 + (0.015 * pow2(L_primeBar - 50.0)) / std::sqrt(20.0 + pow2(L_primeBar - 50.0));
569  const double S_C = 1.0 + 0.045 * C_primeBar;
570  const double S_H = 1.0 + 0.015 * C_primeBar * T;
571  const double R_T = -R_C * sinDegree(60.0 * std::exp(-pow2((h_primeBar - 275) / 25.0)));
572 
573  constexpr double kL = 1.0;
574  constexpr double kC = 1.0;
575  constexpr double kH = 1.0;
576 
577  const double deltaL = deltaL_prime / (kL * S_L);
578  const double deltaC = deltaC_prime / (kC * S_C);
579  const double deltaH = deltaH_prime / (kH * S_H);
580 
581  return /*std::sqrt*/ (deltaL * deltaL + deltaC * deltaC + deltaH * deltaH + R_T * deltaC * deltaH);
582 }
583 
584 struct AnsiBuffer {
585  using ColorCache = QHash<QRgb, int>;
586 
587  void append(char c)
588  {
589  Q_ASSERT(remaining() >= 1);
590  m_data[m_size] = c;
591  ++m_size;
592  }
593 
594  void append(QLatin1String str)
595  {
596  Q_ASSERT(remaining() >= str.size());
597  memcpy(m_data + m_size, str.data(), str.size());
598  m_size += str.size();
599  }
600 
601  void appendForeground(QRgb rgb, bool is256Colors, ColorCache &colorCache)
602  {
603  append(QLatin1String("38;"));
604  append(rgb, is256Colors, colorCache);
605  }
606 
607  void appendBackground(QRgb rgb, bool is256Colors, ColorCache &colorCache)
608  {
609  append(QLatin1String("48;"));
610  append(rgb, is256Colors, colorCache);
611  }
612 
613  void append(QRgb rgb, bool is256Colors, ColorCache &colorCache)
614  {
615  auto appendUInt8 = [&](int x) {
616  Q_ASSERT(x <= 255 && x >= 0);
617  if (x > 99) {
618  if (x >= 200) {
619  append('2');
620  x -= 200;
621  } else {
622  append('1');
623  x -= 100;
624  }
625  } else if (x < 10) {
626  append(char('0' + x));
627  return;
628  }
629 
630  // clang-format off
631  constexpr char const* tb2digits =
632  "00" "01" "02" "03" "04" "05" "06" "07" "08" "09"
633  "10" "11" "12" "13" "14" "15" "16" "17" "18" "19"
634  "20" "21" "22" "23" "24" "25" "26" "27" "28" "29"
635  "30" "31" "32" "33" "34" "35" "36" "37" "38" "39"
636  "40" "41" "42" "43" "44" "45" "46" "47" "48" "49"
637  "50" "51" "52" "53" "54" "55" "56" "57" "58" "59"
638  "60" "61" "62" "63" "64" "65" "66" "67" "68" "69"
639  "70" "71" "72" "73" "74" "75" "76" "77" "78" "79"
640  "80" "81" "82" "83" "84" "85" "86" "87" "88" "89"
641  "90" "91" "92" "93" "94" "95" "96" "97" "98" "99";
642  // clang-format on
643 
644  auto *p = tb2digits + x * 2;
645  append(p[0]);
646  append(p[1]);
647  };
648 
649  if (is256Colors) {
650  double dist = 1e24;
651  int idx = 0;
652  auto it = colorCache.find(rgb);
653  if (it == colorCache.end()) {
654  const auto lab = rgbToOklab(rgb);
655  // find the nearest xterm color
656  for (Lab const &xtermLab : xterm240_Oklabs) {
657  auto dist2 = calculate_CIEDE2000(lab, xtermLab);
658  if (dist2 < dist) {
659  dist = dist2;
660  idx = &xtermLab - xterm240_Oklabs;
661  }
662  }
663  // add 16 to convert 240 colors mode to 256 colors mode
664  idx += 16;
665  colorCache.insert(rgb, idx);
666  } else {
667  idx = it.value();
668  }
669 
670  append('5');
671  append(';');
672  appendUInt8(idx);
673  } else {
674  append('2');
675  append(';');
676  appendUInt8(qRed(rgb));
677  append(';');
678  appendUInt8(qGreen(rgb));
679  append(';');
680  appendUInt8(qBlue(rgb));
681  }
682  append(';');
683  }
684 
685  // Replace last character with 'm'. Last character must be ';'
686  void setFinalStyle()
687  {
688  Q_ASSERT(m_data[m_size - 1] == ';');
689  m_data[m_size - 1] = 'm';
690  }
691 
692  void clear()
693  {
694  m_size = 0;
695  }
696 
697  QLatin1String latin1() const
698  {
699  return QLatin1String(m_data, m_size);
700  }
701 
702 private:
703  char m_data[128];
704  int m_size = 0;
705 
706  int remaining() const noexcept
707  {
708  return 128 - m_size;
709  }
710 };
711 
712 void fillString(QString &s, int n, QStringView fill)
713 {
714  if (n > 0) {
715  for (; n > fill.size(); n -= fill.size()) {
716  s += fill;
717  }
718  s += fill.left(n);
719  }
720 }
721 
722 struct GraphLine {
723  QString graphLine;
724  QString labelLine;
725  int graphLineLength = 0;
726  int labelLineLength = 0;
727  int nextLabelOffset = 0;
728 
729  template<class String>
730  void pushLabel(int offset, String const &s, int numberDisplayableChar)
731  {
732  Q_ASSERT(offset >= labelLineLength);
733  const int n = offset - labelLineLength;
734  labelLineLength += numberDisplayableChar + n;
735  fillLine(labelLine, n);
736  labelLine += s;
737  nextLabelOffset = labelLineLength;
738  }
739 
740  template<class String>
741  void pushGraph(int offset, String const &s, int numberDisplayableChar)
742  {
743  Q_ASSERT(offset >= graphLineLength);
744  const int n = offset - graphLineLength;
745  graphLineLength += numberDisplayableChar + n;
746  fillLine(graphLine, n);
747  const int ps1 = graphLine.size();
748  graphLine += s;
749  if (offset >= labelLineLength) {
750  const int n2 = offset - labelLineLength;
751  labelLineLength += n2 + 1;
752  fillLine(labelLine, n2);
753  labelLine += QStringView(graphLine).right(graphLine.size() - ps1);
754  }
755  }
756 
757 private:
758  static void fillLine(QString &s, int n)
759  {
760  Q_ASSERT(n >= 0);
761  fillString(s,
762  n,
763  QStringLiteral(" "
764  " "
765  " "));
766  }
767 };
768 
769 /**
770  * Returns the first free line at a given position or create a new one
771  */
772 GraphLine &lineAtOffset(std::vector<GraphLine> &graphLines, int offset)
773 {
774  const auto last = graphLines.end();
775  auto p = std::find_if(graphLines.begin(), last, [=](GraphLine const &line) {
776  return line.nextLabelOffset < offset;
777  });
778  if (p == last) {
779  graphLines.emplace_back();
780  return graphLines.back();
781  }
782  return *p;
783 }
784 
785 // disable bold, italic and underline on |
786 const QLatin1String graphSymbol("\x1b[21;23;24m|");
787 // reverse video
788 const QLatin1String nameStyle("\x1b[7m");
789 
790 /**
791  * ANSI Highlighter dedicated to traces
792  */
793 class DebugSyntaxHighlighter : public KSyntaxHighlighting::AbstractHighlighter
794 {
795 public:
796  using TraceOption = KSyntaxHighlighting::AnsiHighlighter::TraceOption;
798 
799  void setDefinition(const KSyntaxHighlighting::Definition &def) override
800  {
802  m_contextCapture.setDefinition(def);
803 
804  const auto &definitions = def.includedDefinitions();
805  for (const auto &definition : definitions) {
806  const auto *defData = DefinitionData::get(definition);
807  for (const auto &context : defData->contexts) {
808  m_defDataBycontexts.insert(&context, defData);
809  }
810  }
811  }
812 
813  void highlightData(QTextStream &in,
814  QTextStream &out,
815  QLatin1String infoStyle,
816  QLatin1String editorBackground,
817  const std::vector<QPair<QString, QString>> &ansiStyles,
818  TraceOptions traceOptions)
819  {
820  initRegionStyles(ansiStyles);
821 
822  m_hasFormatTrace = traceOptions.testFlag(TraceOption::Format);
823  m_hasRegionTrace = traceOptions.testFlag(TraceOption::Region);
824  m_hasStackSizeTrace = traceOptions.testFlag(TraceOption::StackSize);
825  m_hasContextTrace = traceOptions.testFlag(TraceOption::Context);
826  const bool hasFormatOrContextTrace = m_hasFormatTrace || m_hasContextTrace || m_hasStackSizeTrace;
827 
828  const bool hasSeparator = hasFormatOrContextTrace && m_hasRegionTrace;
829  const QString resetBgColor = (editorBackground.isEmpty() ? QStringLiteral("\x1b[0m") : editorBackground);
830 
831  bool firstLine = true;
832  State state;
833  QString currentLine;
834  while (in.readLineInto(&currentLine)) {
835  auto oldState = state;
836  state = highlightLine(currentLine, state);
837 
838  if (hasSeparator) {
839  if (!firstLine) {
840  out << QStringLiteral("\x1b[0m────────────────────────────────────────────────────\x1b[K\n");
841  }
842  firstLine = false;
843  }
844 
845  if (!m_regions.empty()) {
846  printRegions(out, infoStyle, currentLine.size());
847  out << resetBgColor;
848  }
849 
850  for (const auto &fragment : m_highlightedFragments) {
851  auto const &ansiStyle = ansiStyles[fragment.formatId];
852  out << ansiStyle.first << QStringView(currentLine).mid(fragment.offset, fragment.length) << ansiStyle.second;
853  }
854 
855  out << QStringLiteral("\x1b[K\n");
856 
857  if (hasFormatOrContextTrace && !m_highlightedFragments.empty()) {
858  if (m_hasContextTrace || m_hasStackSizeTrace) {
859  appendContextNames(oldState, currentLine);
860  }
861 
862  printFormats(out, infoStyle, ansiStyles);
863  out << resetBgColor;
864  }
865 
866  m_highlightedFragments.clear();
867  }
868  }
869 
870  void applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) override
871  {
872  m_highlightedFragments.push_back({m_hasFormatTrace ? format.name() : QString(), offset, length, format.id()});
873  }
874 
875  void applyFolding(int offset, int /*length*/, FoldingRegion region) override
876  {
877  if (!m_hasRegionTrace) {
878  return;
879  }
880 
881  const auto id = region.id();
882 
883  if (region.type() == FoldingRegion::Begin) {
884  m_regions.push_back(Region{m_regionDepth, offset, -1, id, Region::State::Open});
885  // swap with previous region if this is a closing region with same offset in order to superimpose labels
886  if (m_regions.size() >= 2) {
887  auto &previousRegion = m_regions[m_regions.size() - 2];
888  if (previousRegion.state == Region::State::Close && previousRegion.offset == offset) {
889  std::swap(previousRegion, m_regions.back());
890  if (previousRegion.bindIndex != -1) {
891  m_regions[previousRegion.bindIndex].bindIndex = m_regions.size() - 1;
892  }
893  }
894  }
895  ++m_regionDepth;
896  } else {
897  // find open region
898  auto it = m_regions.rbegin();
899  auto eit = m_regions.rend();
900  for (int depth = 0; it != eit; ++it) {
901  if (it->regionId == id && it->bindIndex < 0) {
902  if (it->state == Region::State::Close) {
903  ++depth;
904  } else if (--depth < 0) {
905  break;
906  }
907  }
908  }
909 
910  if (it != eit) {
911  it->bindIndex = int(m_regions.size());
912  int bindIndex = int(&*it - m_regions.data());
913  m_regions.push_back(Region{it->depth, offset, bindIndex, id, Region::State::Close});
914  } else {
915  m_regions.push_back(Region{-1, offset, -1, id, Region::State::Close});
916  }
917 
918  m_regionDepth = std::max(m_regionDepth - 1, 0);
919  }
920  }
921 
923 
924 private:
925  /**
926  * Initializes with colors of \p ansiStyle without duplicate.
927  */
928  void initRegionStyles(const std::vector<QPair<QString, QString>> &ansiStyles)
929  {
930  m_regionStyles.resize(ansiStyles.size());
931  for (std::size_t i = 0; i < m_regionStyles.size(); ++i) {
932  m_regionStyles[i] = ansiStyles[i].first;
933  }
934 
935  std::sort(m_regionStyles.begin(), m_regionStyles.end());
936  m_regionStyles.erase(std::unique(m_regionStyles.begin(), m_regionStyles.end()), m_regionStyles.end());
937  }
938 
939  /**
940  * Append the context name in front of the format name.
941  */
942  void appendContextNames(State &state, QStringView currentLine)
943  {
944  auto newState = state;
945  for (auto &fragment : m_highlightedFragments) {
946  QString contextName = extractContextName(StateData::get(newState));
947 
948  m_contextCapture.offsetNext = 0;
949  m_contextCapture.lengthNext = 0;
950  // truncate the line to deduce the context from the format
951  const auto lineFragment = currentLine.mid(0, fragment.offset + fragment.length + 1);
952  newState = m_contextCapture.highlightLine(lineFragment, state);
953 
954  // Deduced context does not start at the position of the format.
955  // This can happen because of lookAhead/fallthrought attribute,
956  // assertion in regex, etc.
957  if (m_contextCapture.offset != fragment.offset && m_contextCapture.length != fragment.length) {
958  contextName.insert(0, QLatin1Char('~'));
959  }
960  fragment.name.insert(0, contextName);
961  }
962  }
963 
964  /**
965  * \return Current context name with definition name if different
966  * from the current definition name
967  */
968  QString extractContextName(StateData *stateData) const
969  {
970  QString label;
971 
972  if (m_hasStackSizeTrace) {
973  label += QLatin1Char('(') % QString::number(stateData->size()) % QLatin1Char(')');
974  }
975 
976  if (m_hasContextTrace) {
977  // first state is empty
978  if (stateData->isEmpty()) {
979  return label + QStringLiteral("[???]");
980  }
981 
982  const auto context = stateData->topContext();
983  const auto defDataIt = m_defDataBycontexts.find(context);
984  const auto contextName = (defDataIt != m_defDataBycontexts.end()) ? QString(QLatin1Char('<') % (*defDataIt)->name % QLatin1Char('>')) : QString();
985  return QString(label % contextName % QLatin1Char('[') % context->name() % QLatin1Char(']'));
986  }
987 
988  return label;
989  }
990 
991  void printFormats(QTextStream &out, QLatin1String regionStyle, const std::vector<QPair<QString, QString>> &ansiStyles)
992  {
993  // init graph
994  m_formatGraph.clear();
995  for (auto const &fragment : m_highlightedFragments) {
996  GraphLine &line = lineAtOffset(m_formatGraph, fragment.offset);
997  auto const &style = ansiStyles[fragment.formatId].first;
998  line.pushLabel(fragment.offset, style % nameStyle % fragment.name % regionStyle, fragment.name.size());
999 
1000  for (GraphLine *pline = m_formatGraph.data(); pline <= &line; ++pline) {
1001  pline->pushGraph(fragment.offset, style % graphSymbol % regionStyle, 1);
1002  }
1003  }
1004 
1005  // display graph
1006  out << regionStyle;
1007  auto first = m_formatGraph.begin();
1008  auto last = m_formatGraph.end();
1009  --last;
1010  for (; first != last; ++first) {
1011  out << first->graphLine << "\x1b[K\n" << first->labelLine << "\x1b[K\n";
1012  }
1013  out << first->graphLine << "\x1b[K\n" << first->labelLine << "\x1b[K\x1b[0m\n";
1014  }
1015 
1016  void printRegions(QTextStream &out, QLatin1String regionStyle, int lineLength)
1017  {
1018  const QString continuationLine = QStringLiteral(
1019  "------------------------------"
1020  "------------------------------"
1021  "------------------------------");
1022 
1023  bool hasContinuation = false;
1024 
1025  m_regionGraph.clear();
1026  QString label;
1027  QString numStr;
1028 
1029  // init graph
1030  for (Region &region : m_regions) {
1031  if (region.state == Region::State::Continuation) {
1032  hasContinuation = true;
1033  continue;
1034  }
1035 
1036  auto pushGraphs = [&](int offset, const GraphLine *endline, QStringView style) {
1037  for (GraphLine *pline = m_regionGraph.data(); pline <= endline; ++pline) {
1038  // a label can hide a graph
1039  if (pline->graphLineLength <= offset) {
1040  pline->pushGraph(offset, style % graphSymbol % regionStyle, 1);
1041  }
1042  }
1043  };
1044 
1045  QChar openChar;
1046  QChar closeChar;
1047  int lpad = 0;
1048  int rpad = 0;
1049 
1050  int offsetLabel = region.offset;
1051 
1052  numStr.setNum(region.regionId);
1053 
1054  if (region.state == Region::State::Open) {
1055  openChar = QLatin1Char('(');
1056  if (region.bindIndex == -1) {
1057  rpad = lineLength - region.offset - numStr.size();
1058  } else {
1059  rpad = m_regions[region.bindIndex].offset - region.offset - 2;
1060  closeChar = QLatin1Char(')');
1061  }
1062  // close without open
1063  } else if (region.bindIndex == -1) {
1064  closeChar = QLatin1Char('>');
1065  // label already present, we only display the graph
1066  } else if (m_regions[region.bindIndex].state == Region::State::Open) {
1067  const auto &openRegion = m_regions[region.bindIndex];
1068  // here offset is a graph index
1069  const GraphLine &line = m_regionGraph[openRegion.offset];
1070  const auto &style = m_regionStyles[openRegion.depth % m_regionStyles.size()];
1071  pushGraphs(region.offset, &line, style);
1072  continue;
1073  } else {
1074  closeChar = QLatin1Char(')');
1075  lpad = region.offset - numStr.size();
1076  offsetLabel = 0;
1077  }
1078 
1079  const QStringView openS(&openChar, openChar.unicode() ? 1 : 0);
1080  const QStringView closeS(&closeChar, closeChar.unicode() ? 1 : 0);
1081 
1082  label.clear();
1083  fillString(label, lpad, continuationLine);
1084  label += numStr;
1085  fillString(label, rpad, continuationLine);
1086 
1087  GraphLine &line = lineAtOffset(m_regionGraph, offsetLabel);
1088  const auto &style = m_regionStyles[region.depth % m_regionStyles.size()];
1089  line.pushLabel(offsetLabel, style % nameStyle % openS % label % closeS % regionStyle, label.size() + openS.size() + closeS.size());
1090  pushGraphs(region.offset, &line, style);
1091 
1092  // transforms offset into graph index when region is on 1 line
1093  if (region.state == Region::State::Open && region.bindIndex != -1) {
1094  region.offset = &line - m_regionGraph.data();
1095  }
1096  }
1097 
1098  out << regionStyle;
1099 
1100  // display regions which are neither closed nor open
1101  if (hasContinuation) {
1102  label.clear();
1103  fillString(label, lineLength ? lineLength : 5, continuationLine);
1104  for (const auto &region : m_regions) {
1105  if (region.state == Region::State::Continuation && region.bindIndex == -1) {
1106  const auto &style = m_regionStyles[region.depth % m_regionStyles.size()];
1107  out << style << nameStyle << label << regionStyle << "\x1b[K\n";
1108  }
1109  }
1110  }
1111 
1112  // display graph
1113  if (!m_regionGraph.empty()) {
1114  auto first = m_regionGraph.rbegin();
1115  auto last = m_regionGraph.rend();
1116  --last;
1117  for (; first != last; ++first) {
1118  out << first->labelLine << "\x1b[K\n" << first->graphLine << "\x1b[K\n";
1119  }
1120  out << first->labelLine << "\x1b[K\n" << first->graphLine << "\x1b[K\x1b[0m\n";
1121  }
1122 
1123  // keep regions that are not closed
1124  m_regions.erase(std::remove_if(m_regions.begin(),
1125  m_regions.end(),
1126  [](Region const &region) {
1127  return region.bindIndex != -1 || region.state == Region::State::Close;
1128  }),
1129  m_regions.end());
1130  // all remaining regions become Continuation
1131  for (auto &region : m_regions) {
1132  region.offset = 0;
1133  region.state = Region::State::Continuation;
1134  }
1135  }
1136 
1137  struct HighlightFragment {
1138  QString name;
1139  int offset;
1140  int length;
1141  quint16 formatId;
1142  };
1143 
1144  struct ContextCaptureHighlighter : KSyntaxHighlighting::AbstractHighlighter {
1145  int offset;
1146  int length;
1147  int offsetNext;
1148  int lengthNext;
1149 
1150  void applyFormat(int offset, int length, const KSyntaxHighlighting::Format & /*format*/) override
1151  {
1152  offset = offsetNext;
1153  length = lengthNext;
1154  offsetNext = offset;
1155  lengthNext = length;
1156  }
1157 
1159  };
1160 
1161  struct Region {
1162  enum class State : int8_t {
1163  Open,
1164  Close,
1165  Continuation,
1166  };
1167 
1168  int depth;
1169  int offset;
1170  int bindIndex;
1171  quint16 regionId;
1172  State state;
1173  };
1174 
1175  bool m_hasFormatTrace;
1176  bool m_hasRegionTrace;
1177  bool m_hasStackSizeTrace;
1178  bool m_hasContextTrace;
1179 
1180  std::vector<HighlightFragment> m_highlightedFragments;
1181  std::vector<GraphLine> m_formatGraph;
1182  ContextCaptureHighlighter m_contextCapture;
1183 
1184  int m_regionDepth = 0;
1185  std::vector<Region> m_regions;
1186  std::vector<GraphLine> m_regionGraph;
1187  std::vector<QStringView> m_regionStyles;
1188 
1190 };
1191 } // anonymous namespace
1192 
1193 class KSyntaxHighlighting::AnsiHighlighterPrivate
1194 {
1195 public:
1196  QTextStream out;
1197  QFile file;
1198  QStringView currentLine;
1199  // pairs of startColor / resetColor
1200  std::vector<QPair<QString, QString>> ansiStyles;
1201 };
1202 
1203 AnsiHighlighter::AnsiHighlighter()
1204  : d(new AnsiHighlighterPrivate())
1205 {
1206 }
1207 
1208 AnsiHighlighter::~AnsiHighlighter() = default;
1209 
1210 void AnsiHighlighter::setOutputFile(const QString &fileName)
1211 {
1212  if (d->file.isOpen()) {
1213  d->file.close();
1214  }
1215  d->file.setFileName(fileName);
1216  if (!d->file.open(QFile::WriteOnly | QFile::Truncate)) {
1217  qCWarning(Log) << "Failed to open output file" << fileName << ":" << d->file.errorString();
1218  return;
1219  }
1220  d->out.setDevice(&d->file);
1221 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1222  d->out.setCodec("UTF-8");
1223 #endif
1224 }
1225 
1226 void AnsiHighlighter::setOutputFile(FILE *fileHandle)
1227 {
1228  d->file.open(fileHandle, QIODevice::WriteOnly);
1229  d->out.setDevice(&d->file);
1230 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1231  d->out.setCodec("UTF-8");
1232 #endif
1233 }
1234 
1235 void AnsiHighlighter::highlightFile(const QString &fileName, AnsiFormat format, bool useEditorBackground, TraceOptions traceOptions)
1236 {
1237  QFileInfo fi(fileName);
1238  QFile f(fileName);
1239  if (!f.open(QFile::ReadOnly)) {
1240  qCWarning(Log) << "Failed to open input file" << fileName << ":" << f.errorString();
1241  return;
1242  }
1243 
1244  highlightData(&f, format, useEditorBackground, traceOptions);
1245 }
1246 
1247 void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, bool useEditorBackground, TraceOptions traceOptions)
1248 {
1249  if (!d->out.device()) {
1250  qCWarning(Log) << "No output stream defined!";
1251  return;
1252  }
1253 
1254  const auto is256Colors = (format == AnsiFormat::XTerm256Color);
1255  const auto theme = this->theme();
1256  const auto definition = this->definition();
1257 
1258  auto definitions = definition.includedDefinitions();
1259  definitions.append(definition);
1260 
1261  AnsiBuffer::ColorCache colorCache;
1262 
1263  AnsiBuffer foregroundColorBuffer;
1264  AnsiBuffer backgroundColorBuffer;
1265  QLatin1String foregroundDefaultColor;
1266  QLatin1String backgroundDefaultColor;
1267 
1268  // https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
1269 
1270  if (useEditorBackground) {
1271  const QRgb foregroundColor = theme.textColor(Theme::Normal);
1272  const QRgb backgroundColor = theme.editorColor(Theme::BackgroundColor);
1273  foregroundColorBuffer.appendForeground(foregroundColor, is256Colors, colorCache);
1274  backgroundColorBuffer.append(QLatin1String("\x1b["));
1275  backgroundColorBuffer.appendBackground(backgroundColor, is256Colors, colorCache);
1276  foregroundDefaultColor = foregroundColorBuffer.latin1();
1277  backgroundDefaultColor = backgroundColorBuffer.latin1().mid(2);
1278  }
1279 
1280  // ansiStyles must not be empty for applyFormat to work even with a definition without any context
1281  if (d->ansiStyles.empty()) {
1282  d->ansiStyles.resize(32);
1283  } else {
1284  d->ansiStyles[0].first.clear();
1285  d->ansiStyles[0].second.clear();
1286  }
1287 
1288  // initialize ansiStyles
1289  for (auto &&definition : std::as_const(definitions)) {
1290  for (auto &&format : std::as_const(DefinitionData::get(definition)->formats)) {
1291  const auto id = format.id();
1292  if (id >= d->ansiStyles.size()) {
1293  // better than id + 1 to avoid successive allocations
1294  d->ansiStyles.resize(id * 2);
1295  }
1296 
1297  AnsiBuffer buffer;
1298 
1299  buffer.append(QLatin1String("\x1b["));
1300 
1301  const bool hasFg = format.hasTextColor(theme);
1302  const bool hasBg = format.hasBackgroundColor(theme);
1303  const bool hasBold = format.isBold(theme);
1304  const bool hasItalic = format.isItalic(theme);
1305  const bool hasUnderline = format.isUnderline(theme);
1306  const bool hasStrikeThrough = format.isStrikeThrough(theme);
1307 
1308  if (hasFg) {
1309  buffer.appendForeground(format.textColor(theme).rgb(), is256Colors, colorCache);
1310  } else {
1311  buffer.append(foregroundDefaultColor);
1312  }
1313  if (hasBg) {
1314  buffer.appendBackground(format.backgroundColor(theme).rgb(), is256Colors, colorCache);
1315  }
1316  if (hasBold) {
1317  buffer.append(QLatin1String("1;"));
1318  }
1319  if (hasItalic) {
1320  buffer.append(QLatin1String("3;"));
1321  }
1322  if (hasUnderline) {
1323  buffer.append(QLatin1String("4;"));
1324  }
1325  if (hasStrikeThrough) {
1326  buffer.append(QLatin1String("9;"));
1327  }
1328 
1329  // if there is ANSI style
1330  if (buffer.latin1().size() > 2) {
1331  buffer.setFinalStyle();
1332  d->ansiStyles[id].first = buffer.latin1();
1333 
1334  if (useEditorBackground) {
1335  buffer.clear();
1336  const bool hasEffect = hasBold || hasItalic || hasUnderline || hasStrikeThrough;
1337  if (hasBg) {
1338  buffer.append(hasEffect ? QLatin1String("\x1b[0;") : QLatin1String("\x1b["));
1339  buffer.append(backgroundDefaultColor);
1340  buffer.setFinalStyle();
1341  d->ansiStyles[id].second = buffer.latin1();
1342  } else if (hasEffect) {
1343  buffer.append(QLatin1String("\x1b["));
1344  if (hasBold) {
1345  buffer.append(QLatin1String("21;"));
1346  }
1347  if (hasItalic) {
1348  buffer.append(QLatin1String("23;"));
1349  }
1350  if (hasUnderline) {
1351  buffer.append(QLatin1String("24;"));
1352  }
1353  if (hasStrikeThrough) {
1354  buffer.append(QLatin1String("29;"));
1355  }
1356  buffer.setFinalStyle();
1357  d->ansiStyles[id].second = buffer.latin1();
1358  }
1359  } else {
1360  d->ansiStyles[id].second = QStringLiteral("\x1b[0m");
1361  }
1362  }
1363  }
1364  }
1365 
1366  if (useEditorBackground) {
1367  backgroundColorBuffer.setFinalStyle();
1368  backgroundDefaultColor = backgroundColorBuffer.latin1();
1369  d->out << backgroundDefaultColor;
1370  }
1371 
1372  QTextStream in(dev);
1373 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1374  in.setCodec("UTF-8");
1375 #endif
1376 
1377  if (!traceOptions) {
1378  State state;
1379  QString currentLine;
1380  while (in.readLineInto(&currentLine)) {
1381  d->currentLine = currentLine;
1382  state = highlightLine(d->currentLine, state);
1383 
1384  if (useEditorBackground) {
1385  d->out << QStringLiteral("\x1b[K\n");
1386  } else {
1387  d->out << QLatin1Char('\n');
1388  }
1389  }
1390  } else {
1391  AnsiBuffer buffer;
1392  buffer.append(QLatin1String("\x1b[0;"));
1393  buffer.appendBackground(theme.editorColor(useEditorBackground ? Theme::TemplateBackground : Theme::BackgroundColor), is256Colors, colorCache);
1394  buffer.setFinalStyle();
1395  DebugSyntaxHighlighter debugHighlighter;
1396  debugHighlighter.setDefinition(definition);
1397  debugHighlighter.highlightData(in, d->out, buffer.latin1(), backgroundDefaultColor, d->ansiStyles, traceOptions);
1398  }
1399 
1400  if (useEditorBackground) {
1401  d->out << QStringLiteral("\x1b[0m");
1402  }
1403 
1404  d->out.setDevice(nullptr);
1405  d->file.close();
1406  d->ansiStyles.clear();
1407 }
1408 
1409 void AnsiHighlighter::applyFormat(int offset, int length, const Format &format)
1410 {
1411  auto const &ansiStyle = d->ansiStyles[format.id()];
1412  d->out << ansiStyle.first << d->currentLine.mid(offset, length) << ansiStyle.second;
1413 }
Opaque handle to the state of the highlighting engine.
Definition: state.h:25
Represents a begin or end of a folding region.
Definition: foldingregion.h:18
QString name() const
The name of this format as used in the syntax definition file.
Definition: format.cpp:96
QString number(int n, int base)
QStringView right(qsizetype length) const const
int size() const const
quint16 id() const
Returns a unique identifier of this format.
Definition: format.cpp:101
QString::reverse_iterator rbegin()
void clear()
QStringView mid(qsizetype start) const const
Describes the format to be used for a specific text fragment.
Definition: format.h:33
Abstract base class for highlighters.
QStringView left(qsizetype length) const const
qsizetype size() const const
KGuiItem clear()
QString & setNum(short n, int base)
bool isEmpty() const const
QVector< Definition > includedDefinitions() const
Returns a list of Definitions that are referenced with the IncludeRules rule.
Definition: definition.cpp:234
QString label(StandardShortcut id)
@ TemplateBackground
Background color for text templates (snippets).
Definition: theme.h:219
QString & insert(int position, QChar ch)
State highlightLine(QStringView text, const State &state)
Highlight the given line.
Type type() const
Returns whether this is the begin or end of a region.
QString name(StandardShortcut id)
virtual void setDefinition(const Definition &def)
Sets the syntax definition used for highlighting.
@ Normal
Default text style for normal text and source code without special highlighting.
Definition: theme.h:83
const char * data() const const
@ BackgroundColor
Background color for the editing area.
Definition: theme.h:165
quint16 id() const
Returns a unique identifier for this folding region.
bool readLineInto(QString *line, qint64 maxlen)
int size() const const
void setCodec(QTextCodec *codec)
@ Begin
Indicates the start of a FoldingRegion.
Definition: foldingregion.h:28
Represents a syntax definition.
Definition: definition.h:86
ushort unicode() const const
virtual QVariant get(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Mar 26 2023 04:09:17 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.