KSyntaxHighlighting

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

KDE's Doxygen guidelines are available online.