File indexing completed on 2024-05-12 15:50:03

0001 /*
0002     SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com>
0003 
0004     SPDX-License-Identifier: MIT
0005 */
0006 
0007 #include "ansihighlighter.h"
0008 #include "context_p.h"
0009 #include "definition.h"
0010 #include "definition_p.h"
0011 #include "format.h"
0012 #include "ksyntaxhighlighting_logging.h"
0013 #include "state.h"
0014 #include "state_p.h"
0015 #include "theme.h"
0016 
0017 #include <QColor>
0018 #include <QFile>
0019 #include <QFileInfo>
0020 #include <QHash>
0021 #include <QTextStream>
0022 
0023 #include <cmath>
0024 #include <vector>
0025 
0026 using namespace KSyntaxHighlighting;
0027 
0028 namespace
0029 {
0030 struct Lab {
0031     double L;
0032     double a;
0033     double b;
0034 };
0035 
0036 // clang-format off
0037     // xterm color reference
0038     // constexpr Rgb888 xterm256Colors[] {
0039     //     {0x00, 0x00, 0x00}, {0x80, 0x00, 0x00}, {0x00, 0x80, 0x00}, {0x80, 0x80, 0x00},
0040     //     {0x00, 0x00, 0x80}, {0x80, 0x00, 0x80}, {0x00, 0x80, 0x80}, {0xc0, 0xc0, 0xc0},
0041     //     {0x80, 0x80, 0x80}, {0xff, 0x00, 0x00}, {0x00, 0xff, 0x00}, {0xff, 0xff, 0x00},
0042     //     {0x00, 0x00, 0xff}, {0xff, 0x00, 0xff}, {0x00, 0xff, 0xff}, {0xff, 0xff, 0xff},
0043     //     {0x00, 0x00, 0x00}, {0x00, 0x00, 0x5f}, {0x00, 0x00, 0x87}, {0x00, 0x00, 0xaf},
0044     //     {0x00, 0x00, 0xd7}, {0x00, 0x00, 0xff}, {0x00, 0x5f, 0x00}, {0x00, 0x5f, 0x5f},
0045     //     {0x00, 0x5f, 0x87}, {0x00, 0x5f, 0xaf}, {0x00, 0x5f, 0xd7}, {0x00, 0x5f, 0xff},
0046     //     {0x00, 0x87, 0x00}, {0x00, 0x87, 0x5f}, {0x00, 0x87, 0x87}, {0x00, 0x87, 0xaf},
0047     //     {0x00, 0x87, 0xd7}, {0x00, 0x87, 0xff}, {0x00, 0xaf, 0x00}, {0x00, 0xaf, 0x5f},
0048     //     {0x00, 0xaf, 0x87}, {0x00, 0xaf, 0xaf}, {0x00, 0xaf, 0xd7}, {0x00, 0xaf, 0xff},
0049     //     {0x00, 0xd7, 0x00}, {0x00, 0xd7, 0x5f}, {0x00, 0xd7, 0x87}, {0x00, 0xd7, 0xaf},
0050     //     {0x00, 0xd7, 0xd7}, {0x00, 0xd7, 0xff}, {0x00, 0xff, 0x00}, {0x00, 0xff, 0x5f},
0051     //     {0x00, 0xff, 0x87}, {0x00, 0xff, 0xaf}, {0x00, 0xff, 0xd7}, {0x00, 0xff, 0xff},
0052     //     {0x5f, 0x00, 0x00}, {0x5f, 0x00, 0x5f}, {0x5f, 0x00, 0x87}, {0x5f, 0x00, 0xaf},
0053     //     {0x5f, 0x00, 0xd7}, {0x5f, 0x00, 0xff}, {0x5f, 0x5f, 0x00}, {0x5f, 0x5f, 0x5f},
0054     //     {0x5f, 0x5f, 0x87}, {0x5f, 0x5f, 0xaf}, {0x5f, 0x5f, 0xd7}, {0x5f, 0x5f, 0xff},
0055     //     {0x5f, 0x87, 0x00}, {0x5f, 0x87, 0x5f}, {0x5f, 0x87, 0x87}, {0x5f, 0x87, 0xaf},
0056     //     {0x5f, 0x87, 0xd7}, {0x5f, 0x87, 0xff}, {0x5f, 0xaf, 0x00}, {0x5f, 0xaf, 0x5f},
0057     //     {0x5f, 0xaf, 0x87}, {0x5f, 0xaf, 0xaf}, {0x5f, 0xaf, 0xd7}, {0x5f, 0xaf, 0xff},
0058     //     {0x5f, 0xd7, 0x00}, {0x5f, 0xd7, 0x5f}, {0x5f, 0xd7, 0x87}, {0x5f, 0xd7, 0xaf},
0059     //     {0x5f, 0xd7, 0xd7}, {0x5f, 0xd7, 0xff}, {0x5f, 0xff, 0x00}, {0x5f, 0xff, 0x5f},
0060     //     {0x5f, 0xff, 0x87}, {0x5f, 0xff, 0xaf}, {0x5f, 0xff, 0xd7}, {0x5f, 0xff, 0xff},
0061     //     {0x87, 0x00, 0x00}, {0x87, 0x00, 0x5f}, {0x87, 0x00, 0x87}, {0x87, 0x00, 0xaf},
0062     //     {0x87, 0x00, 0xd7}, {0x87, 0x00, 0xff}, {0x87, 0x5f, 0x00}, {0x87, 0x5f, 0x5f},
0063     //     {0x87, 0x5f, 0x87}, {0x87, 0x5f, 0xaf}, {0x87, 0x5f, 0xd7}, {0x87, 0x5f, 0xff},
0064     //     {0x87, 0x87, 0x00}, {0x87, 0x87, 0x5f}, {0x87, 0x87, 0x87}, {0x87, 0x87, 0xaf},
0065     //     {0x87, 0x87, 0xd7}, {0x87, 0x87, 0xff}, {0x87, 0xaf, 0x00}, {0x87, 0xaf, 0x5f},
0066     //     {0x87, 0xaf, 0x87}, {0x87, 0xaf, 0xaf}, {0x87, 0xaf, 0xd7}, {0x87, 0xaf, 0xff},
0067     //     {0x87, 0xd7, 0x00}, {0x87, 0xd7, 0x5f}, {0x87, 0xd7, 0x87}, {0x87, 0xd7, 0xaf},
0068     //     {0x87, 0xd7, 0xd7}, {0x87, 0xd7, 0xff}, {0x87, 0xff, 0x00}, {0x87, 0xff, 0x5f},
0069     //     {0x87, 0xff, 0x87}, {0x87, 0xff, 0xaf}, {0x87, 0xff, 0xd7}, {0x87, 0xff, 0xff},
0070     //     {0xaf, 0x00, 0x00}, {0xaf, 0x00, 0x5f}, {0xaf, 0x00, 0x87}, {0xaf, 0x00, 0xaf},
0071     //     {0xaf, 0x00, 0xd7}, {0xaf, 0x00, 0xff}, {0xaf, 0x5f, 0x00}, {0xaf, 0x5f, 0x5f},
0072     //     {0xaf, 0x5f, 0x87}, {0xaf, 0x5f, 0xaf}, {0xaf, 0x5f, 0xd7}, {0xaf, 0x5f, 0xff},
0073     //     {0xaf, 0x87, 0x00}, {0xaf, 0x87, 0x5f}, {0xaf, 0x87, 0x87}, {0xaf, 0x87, 0xaf},
0074     //     {0xaf, 0x87, 0xd7}, {0xaf, 0x87, 0xff}, {0xaf, 0xaf, 0x00}, {0xaf, 0xaf, 0x5f},
0075     //     {0xaf, 0xaf, 0x87}, {0xaf, 0xaf, 0xaf}, {0xaf, 0xaf, 0xd7}, {0xaf, 0xaf, 0xff},
0076     //     {0xaf, 0xd7, 0x00}, {0xaf, 0xd7, 0x5f}, {0xaf, 0xd7, 0x87}, {0xaf, 0xd7, 0xaf},
0077     //     {0xaf, 0xd7, 0xd7}, {0xaf, 0xd7, 0xff}, {0xaf, 0xff, 0x00}, {0xaf, 0xff, 0x5f},
0078     //     {0xaf, 0xff, 0x87}, {0xaf, 0xff, 0xaf}, {0xaf, 0xff, 0xd7}, {0xaf, 0xff, 0xff},
0079     //     {0xd7, 0x00, 0x00}, {0xd7, 0x00, 0x5f}, {0xd7, 0x00, 0x87}, {0xd7, 0x00, 0xaf},
0080     //     {0xd7, 0x00, 0xd7}, {0xd7, 0x00, 0xff}, {0xd7, 0x5f, 0x00}, {0xd7, 0x5f, 0x5f},
0081     //     {0xd7, 0x5f, 0x87}, {0xd7, 0x5f, 0xaf}, {0xd7, 0x5f, 0xd7}, {0xd7, 0x5f, 0xff},
0082     //     {0xd7, 0x87, 0x00}, {0xd7, 0x87, 0x5f}, {0xd7, 0x87, 0x87}, {0xd7, 0x87, 0xaf},
0083     //     {0xd7, 0x87, 0xd7}, {0xd7, 0x87, 0xff}, {0xd7, 0xaf, 0x00}, {0xd7, 0xaf, 0x5f},
0084     //     {0xd7, 0xaf, 0x87}, {0xd7, 0xaf, 0xaf}, {0xd7, 0xaf, 0xd7}, {0xd7, 0xaf, 0xff},
0085     //     {0xd7, 0xd7, 0x00}, {0xd7, 0xd7, 0x5f}, {0xd7, 0xd7, 0x87}, {0xd7, 0xd7, 0xaf},
0086     //     {0xd7, 0xd7, 0xd7}, {0xd7, 0xd7, 0xff}, {0xd7, 0xff, 0x00}, {0xd7, 0xff, 0x5f},
0087     //     {0xd7, 0xff, 0x87}, {0xd7, 0xff, 0xaf}, {0xd7, 0xff, 0xd7}, {0xd7, 0xff, 0xff},
0088     //     {0xff, 0x00, 0x00}, {0xff, 0x00, 0x5f}, {0xff, 0x00, 0x87}, {0xff, 0x00, 0xaf},
0089     //     {0xff, 0x00, 0xd7}, {0xff, 0x00, 0xff}, {0xff, 0x5f, 0x00}, {0xff, 0x5f, 0x5f},
0090     //     {0xff, 0x5f, 0x87}, {0xff, 0x5f, 0xaf}, {0xff, 0x5f, 0xd7}, {0xff, 0x5f, 0xff},
0091     //     {0xff, 0x87, 0x00}, {0xff, 0x87, 0x5f}, {0xff, 0x87, 0x87}, {0xff, 0x87, 0xaf},
0092     //     {0xff, 0x87, 0xd7}, {0xff, 0x87, 0xff}, {0xff, 0xaf, 0x00}, {0xff, 0xaf, 0x5f},
0093     //     {0xff, 0xaf, 0x87}, {0xff, 0xaf, 0xaf}, {0xff, 0xaf, 0xd7}, {0xff, 0xaf, 0xff},
0094     //     {0xff, 0xd7, 0x00}, {0xff, 0xd7, 0x5f}, {0xff, 0xd7, 0x87}, {0xff, 0xd7, 0xaf},
0095     //     {0xff, 0xd7, 0xd7}, {0xff, 0xd7, 0xff}, {0xff, 0xff, 0x00}, {0xff, 0xff, 0x5f},
0096     //     {0xff, 0xff, 0x87}, {0xff, 0xff, 0xaf}, {0xff, 0xff, 0xd7}, {0xff, 0xff, 0xff},
0097     //     {0x08, 0x08, 0x08}, {0x12, 0x12, 0x12}, {0x1c, 0x1c, 0x1c}, {0x26, 0x26, 0x26},
0098     //     {0x30, 0x30, 0x30}, {0x3a, 0x3a, 0x3a}, {0x44, 0x44, 0x44}, {0x4e, 0x4e, 0x4e},
0099     //     {0x58, 0x58, 0x58}, {0x62, 0x62, 0x62}, {0x6c, 0x6c, 0x6c}, {0x76, 0x76, 0x76},
0100     //     {0x80, 0x80, 0x80}, {0x8a, 0x8a, 0x8a}, {0x94, 0x94, 0x94}, {0x9e, 0x9e, 0x9e},
0101     //     {0xa8, 0xa8, 0xa8}, {0xb2, 0xb2, 0xb2}, {0xbc, 0xbc, 0xbc}, {0xc6, 0xc6, 0xc6},
0102     //     {0xd0, 0xd0, 0xd0}, {0xda, 0xda, 0xda}, {0xe4, 0xe4, 0xe4}, {0xee, 0xee, 0xee},
0103     // };
0104 
0105     // xterm color represented in Oklab
0106     // see rgbToOklab()
0107     constexpr Lab xterm240_Oklabs[] {
0108         // ignore the first 16 colors as they are unpredictable (user configurable)
0109         // {0x0p+0, 0x0p+0, 0x0p+0},
0110         // {0x1.2d5e6bee2c4f6p+5,  0x1.af99c042ea40cp+3,  0x1.e2f6ba84d2d25p+2},
0111         // {0x1.9fcc4f3622914p+5, -0x1.c105bf1d2d218p+3,  0x1.586870aec30a4p+3},
0112         // {0x1.d089126c75579p+5, -0x1.12107f2e3119p+2,   0x1.7d020b82d4b9cp+3},
0113         // {0x1.b1ce15c4fcb51p+4, -0x1.f203762eb1242p+0, -0x1.2b150ae3c14bep+4},
0114         // {0x1.50bc446f31833p+5,  0x1.078a1150b431ap+4, -0x1.44d35b3de7eafp+3},
0115         // {0x1.b27d96eb8f471p+5, -0x1.1ee8867b0e065p+3, -0x1.2f2f18261c69ap+1},
0116         // {0x1.431e523cc2f4dp+6, -0x1.ad694c777b8p-11,  -0x1.c7c3ea0c32ep-8},
0117         // {0x1.dfe5855ae1528p+5, -0x1.3ee1ad40618p-11,  -0x1.5273b9784a3p-8},
0118         // {0x1.f663baac570efp+5,  0x1.67be9f690c994p+4,  0x1.928e76c3750aep+3},
0119         // {0x1.5a92b1ff8af32p+6, -0x1.76441609cfb3ap+4,  0x1.1f1186319beaap+4},
0120         // {0x1.83323c984ee7ap+6, -0x1.c8df58716d4cbp+2,  0x1.3d9335b5d20f9p+4},
0121         // {0x1.69950098864afp+5, -0x1.9f19c42a8c674p+1, -0x1.f293e325bec2ep+4},
0122         // {0x1.18ac5c68852cdp+6,  0x1.b753a9bd5dbeep+4, -0x1.0ebf0dff35bfbp+4},
0123         // {0x1.6a27499e4d3d6p+6, -0x1.de4892c062f8p+3,  -0x1.f96a59bc9de4cp+1},
0124         // {0x1.8ffffbb77c76ap+6, -0x1.09cab717214p-10,  -0x1.1a1aa7765a7p-7},
0125         // 240 colors mode
0126         {0x0p+0,                0x0p+0,                0x0p+0},
0127         {0x1.5f181b2779cap+4,  -0x1.930f78e22f09ap+0, -0x1.e41dbddfca08ap+3},
0128         {0x1.c2d3be821f882p+4, -0x1.02c70dd8af008p+1, -0x1.36d1623919ffp+4},
0129         {0x1.10a39beeb2926p+5, -0x1.38fe38b7dab01p+1, -0x1.77efa95d520b4p+4},
0130         {0x1.3de43fe8d92efp+5, -0x1.6cf188320fff2p+1, -0x1.b655790a27192p+4},
0131         {0x1.69950098864afp+5, -0x1.9f19c42a8c674p+1, -0x1.f293e325bec2ep+4},
0132         {0x1.50853f46a9f9ep+5, -0x1.6b6901a80404fp+3,  0x1.16bdec11e60d8p+3},
0133         {0x1.5fa625f2c3fbcp+5, -0x1.d0691ed5aa7ap+2,  -0x1.eac16e8cb9241p+0},
0134         {0x1.6f4222fc3f0dbp+5, -0x1.61b0e7ffa5e8ap+2, -0x1.05e8906ee23d7p+3},
0135         {0x1.83e99e6187f46p+5, -0x1.1a61c98b895p+2,   -0x1.c3da094bbcf2fp+3},
0136         {0x1.9ca47689a503dp+5, -0x1.e9259e3439104p+1, -0x1.396812c634de3p+4},
0137         {0x1.b872b55db6144p+5, -0x1.ccd50a2fd6662p+1, -0x1.89dec898ec996p+4},
0138         {0x1.b01d15d276ef1p+5, -0x1.d2a4448f6ccacp+3,  0x1.65ec167dcb488p+3},
0139         {0x1.b9760adf5c444p+5, -0x1.6a3760af4b6c4p+3,  0x1.a26bb322495e2p+1},
0140         {0x1.c38a22a31944p+5,  -0x1.2a2a93c4b741p+3,  -0x1.3b14a376ffbecp+1},
0141         {0x1.d185a36cfcd6ep+5, -0x1.e760f29e8fcb4p+2, -0x1.0bf936f2a6743p+3},
0142         {0x1.e321d754f2b44p+5, -0x1.95a749a95debp+2,  -0x1.c3928e31b9cf8p+3},
0143         {0x1.f7eb8d9ad84b9p+5, -0x1.5e28b7cc193ddp+2, -0x1.38bda7259441ep+4},
0144         {0x1.0552717cdb82p+6,  -0x1.1a33f75f67b0fp+4,  0x1.b0e8bd24c3fdep+3},
0145         {0x1.08898a5b7805dp+6, -0x1.e29bd3071d97ap+3,  0x1.e0b5994e0238ep+2},
0146         {0x1.0c10ad1b87866p+6, -0x1.a54f2215371a5p+3,  0x1.45758e5457898p+1},
0147         {0x1.1111e924447e9p+6, -0x1.68a24e1d5efe1p+3, -0x1.7d178a838ea32p+1},
0148         {0x1.178bb94d30a5cp+6, -0x1.335d7d75dd017p+3, -0x1.13f78c003ba13p+3},
0149         {0x1.1f6aa49fcff2p+6,  -0x1.0830d167759a4p+3, -0x1.c578e33f21d88p+3},
0150         {0x1.30b236b57ac86p+6, -0x1.490afbe3d8e11p+4,  0x1.f8c35fbb689dap+3},
0151         {0x1.331144aae0ad4p+6, -0x1.289bac3eeb83p+4,   0x1.626ea3a63748p+3},
0152         {0x1.35b09f5c62a7ap+6, -0x1.0d33e3db59803p+4,  0x1.b66836d6f7ff6p+2},
0153         {0x1.3973d5b39baa4p+6, -0x1.de87e61f0c86ap+3,  0x1.de8cf8346969cp+0},
0154         {0x1.3e64dbeab2c38p+6, -0x1.a47dea5d85dbbp+3, -0x1.bc586cdcba45p+1},
0155         {0x1.44815d7f73a41p+6, -0x1.706c35b4850ecp+3, -0x1.1d13e731dfc5bp+3},
0156         {0x1.5a92b1ff8af32p+6, -0x1.76441609cfb3ap+4,  0x1.1f1186319beaap+4},
0157         {0x1.5c6885a0ab4cap+6, -0x1.5c0bf4148d03dp+4,  0x1.c5b4a75d0a01ep+3},
0158         {0x1.5e72281eb918cp+6, -0x1.4422b7d2ad44bp+4,  0x1.5305a49da3492p+3},
0159         {0x1.61631d5752788p+6, -0x1.282b31110d79dp+4,  0x1.8b1a60b561753p+2},
0160         {0x1.65488920f4795p+6, -0x1.0b1a0cfacacfbp+4,  0x1.3d72a3bb176fp+0},
0161         {0x1.6a27499e4d3d6p+6, -0x1.de4892c062f8p+3,  -0x1.f96a59bc9de4cp+1},
0162         {0x1.e7d1475ebe201p+4,  0x1.5d4f5ebb6cf8ep+3,  0x1.86e150bac0e61p+2},
0163         {0x1.10883ee613f6bp+5,  0x1.aa957aceb4328p+3, -0x1.06e4a6bcf37ep+3},
0164         {0x1.2a507c82ee1e7p+5,  0x1.880d450b132c9p+3, -0x1.c81b5ba73664ap+3},
0165         {0x1.4902475f20191p+5,  0x1.4f4dfeda4e013p+3, -0x1.38d6c960a7255p+4},
0166         {0x1.6aa42ff68fb65p+5,  0x1.1116140836dp+3,   -0x1.84f2dbb4bf7b2p+4},
0167         {0x1.8def7d6adc3d5p+5,  0x1.ab6406f65a5b9p+2, -0x1.caee116cbc66ep+4},
0168         {0x1.77f724f99d0c9p+5, -0x1.bb9ee0e906bf2p+1,  0x1.345d15707c9e4p+3},
0169         {0x1.8465d178eda3bp+5, -0x1.021519c6d64p-11,  -0x1.11ebea130b3p-8},
0170         {0x1.917d476fba08dp+5,  0x1.84e9b4b9a4816p+0, -0x1.89910d2df7414p+2},
0171         {0x1.a32d016052029p+5,  0x1.352fbc1045df3p+1, -0x1.847eed4ab1e03p+3},
0172         {0x1.b8cfd7baeb72ap+5,  0x1.5d6575ced028ep+1, -0x1.1bff70d82f589p+4},
0173         {0x1.d1a20bfd91dbap+5,  0x1.4de285fe39a4dp+1, -0x1.6f381fca63bcep+4},
0174         {0x1.c99e943a2d79fp+5, -0x1.219ec15c5de22p+3,  0x1.78f2db7cd8205p+3},
0175         {0x1.d20af5c832c06p+5, -0x1.86c31353f2968p+2,  0x1.15f4d958ffb6ep+2},
0176         {0x1.db2d2b76b7db4p+5, -0x1.107aa185fa178p+2, -0x1.3a545f6bb32fep+0},
0177         {0x1.e7ef429658179p+5, -0x1.58a57459e2113p+1, -0x1.c50012145074dp+2},
0178         {0x1.f821bad822a9ep+5, -0x1.8ea06e2a9e1f6p+0, -0x1.9a5cf82ebb612p+3},
0179         {0x1.05b4f2f9696abp+6, -0x1.b3645fe7f9294p-1, -0x1.24f471b4e8b62p+4},
0180         {0x1.0e4a1f1b1ddcep+6, -0x1.b32bbdf7204c1p+3,  0x1.be3e8e0b04a1fp+3},
0181         {0x1.114fcf04f35b3p+6, -0x1.6696d5115c7e4p+3,  0x1.058704ea2708p+3},
0182         {0x1.14a2558b5b6c7p+6, -0x1.2c7e211429d81p+3,  0x1.a9f809f8ee4dfp+1},
0183         {0x1.195c00cf22a51p+6, -0x1.e58de99c40fb6p+2, -0x1.0e4f6479339a5p+1},
0184         {0x1.1f7e4eea78cb7p+6, -0x1.809e6530a25d5p+2, -0x1.ee2cfac349d5ep+2},
0185         {0x1.26f9f5da0f33bp+6, -0x1.2ffe0404fe1e4p+2, -0x1.a87af760f19c2p+3},
0186         {0x1.37635555b270cp+6, -0x1.17deced6d377ep+4,  0x1.0159387931185p+4},
0187         {0x1.39aa8b444a1c7p+6, -0x1.f1b4258b74e35p+3,  0x1.70a1d9ed49333p+3},
0188         {0x1.3c30003dc4bdap+6, -0x1.bcf061cd62971p+3,  0x1.d845b8f464806p+2},
0189         {0x1.3fcf0a7e4fdb8p+6, -0x1.8316f8fb04f1cp+3,  0x1.3be7d797ab219p+1},
0190         {0x1.4492530767fa4p+6, -0x1.4af657f86bc35p+3, -0x1.69ccecfbead6p+1},
0191         {0x1.4a78e7a0304fbp+6, -0x1.18be182ded159p+3, -0x1.07ac09ccdfff4p+3},
0192         {0x1.5fc9ac083946p+6,  -0x1.4f840d59cd9aep+4,  0x1.22ef620ec7775p+4},
0193         {0x1.6192b2ae205fp+6,  -0x1.361d4509435d5p+4,  0x1.cfe893676e62ep+3},
0194         {0x1.638e487d443e1p+6, -0x1.1edcf418396fcp+4,  0x1.5f105a3f7b33p+3},
0195         {0x1.666b5164f3799p+6, -0x1.03988ade0159ep+4,  0x1.a6a2e5baac692p+2},
0196         {0x1.6a3706fa48d42p+6, -0x1.ce6b98f424c54p+3,  0x1.b67fa4fafc2e8p+0},
0197         {0x1.6ef6b7860b53fp+6, -0x1.97ce09961f218p+3, -0x1.b926ed0fbe897p+1},
0198         {0x1.3931bb83cb32dp+5,  0x1.c0894426a198dp+3,  0x1.f5ea328bf4058p+2},
0199         {0x1.4b9e77eb58ebfp+5,  0x1.108cfd41d7919p+4, -0x1.0eaf04c8d3b35p+2},
0200         {0x1.5df2d7bacd40ap+5,  0x1.11e15f9b225acp+4, -0x1.51924b9c514f3p+3},
0201         {0x1.756bc7d79519bp+5,  0x1.03a63c750b36bp+4, -0x1.052fc194cf52p+4},
0202         {0x1.90b3e1d276c5dp+5,  0x1.d79fd75fb3811p+3, -0x1.58e6a7a1da1fcp+4},
0203         {0x1.aea5654d2d631p+5,  0x1.9f14550c88c57p+3, -0x1.a57e86563dbcap+4},
0204         {0x1.9b6948efcb1e8p+5,  0x1.dddaa142e7b3ap+0,  0x1.4f83ba256abcfp+3},
0205         {0x1.a5f9bfdb796c8p+5,  0x1.3de3647070a5bp+2,  0x1.b416b1e2a3817p+0},
0206         {0x1.b1407c7a80b46p+5,  0x1.9d8674f672f17p+2, -0x1.112d7835e0443p+2},
0207         {0x1.c0b6552db89b1p+5,  0x1.d8c2a5262b398p+2, -0x1.480f10f6d0b5fp+3},
0208         {0x1.d3edc2b5ca1f7p+5,  0x1.edbf49a0778e4p+2, -0x1.fe5ca96caae15p+3},
0209         {0x1.ea519f7b4bf8cp+5,  0x1.e35c8bfe38bf9p+2, -0x1.5483ed4b7e5ebp+4},
0210         {0x1.e2c36ca30962cp+5, -0x1.1cd1878e91233p+2,  0x1.8bf558e4fbde3p+3},
0211         {0x1.ea67769abd5c1p+5, -0x1.c053e5b6c3b66p+0,  0x1.59302161a2865p+2},
0212         {0x1.f2ba2dd022991p+5, -0x1.4b64e809e68p-11,  -0x1.5fbb8b338b8p-8},
0213         {0x1.fe6b844bbf20fp+5,  0x1.8040f07e39554p+0, -0x1.7196ffd7de002p+2},
0214         {0x1.06aeef460087dp+6,  0x1.4afa79e390244p+1, -0x1.705a7c8493135p+3},
0215         {0x1.0fa5554a5c955p+6,  0x1.9dea7336ee828p+1, -0x1.1089bb9ca046ep+4},
0216         {0x1.178e9051b853ep+6, -0x1.3b3b8e6c52f1cp+3,  0x1.cc268b9a4e157p+3},
0217         {0x1.1a65db5c4c0d1p+6, -0x1.e697a167b2b86p+2,  0x1.1b4d0d1ca381dp+3},
0218         {0x1.1d86bd983524p+6,  -0x1.77ccb1bcef226p+2,  0x1.08e1384d16e75p+2},
0219         {0x1.21fc7ad985402p+6, -0x1.08e3f099fb00fp+2, -0x1.363e6fbac8902p+0},
0220         {0x1.27cb1d6dd9434p+6, -0x1.4f7ef424b1025p+1, -0x1.b1af4c945ce98p+2},
0221         {0x1.2ee710ec206cfp+6, -0x1.6ac7f2787bda1p+0, -0x1.89e8c18db3849p+3},
0222         {0x1.3e7c86695f695p+6, -0x1.ceac064fd3a97p+3,  0x1.06a67a74ff4cp+4},
0223         {0x1.40ac0504d65f2p+6, -0x1.935bda5136fdbp+3,  0x1.7fa8ea5683dffp+3},
0224         {0x1.4317a9d652f14p+6, -0x1.608608dcc2607p+3,  0x1.fc21edd271f8bp+2},
0225         {0x1.46928cf49114ap+6, -0x1.2883a0410f74bp+3,  0x1.8d4b911a8102bp+1},
0226         {0x1.4b27f53f2d1c8p+6, -0x1.e40f8922e26b2p+2, -0x1.11ca0bf0acee9p+1},
0227         {0x1.50d83b6f7788bp+6, -0x1.82a0417f98419p+2, -0x1.e18432a6676edp+2},
0228         {0x1.6567db05a012cp+6, -0x1.27d55f8b64f16p+4,  0x1.271e9400a42e4p+4},
0229         {0x1.6723b3feab73ap+6, -0x1.0f412c5a09d1ap+4,  0x1.dae43f7f7eb6ep+3},
0230         {0x1.6910cf423acf1p+6, -0x1.f152d3d18c3b3p+3,  0x1.6c04c44876eb2p+3},
0231         {0x1.6bd948eca017fp+6, -0x1.bc29d2c53f57ap+3,  0x1.c44a28d107a62p+2},
0232         {0x1.6f8a69cf10b1p+6,  -0x1.84b224c6d040cp+3,  0x1.1c910c629db67p+1},
0233         {0x1.7429ebc4e406dp+6, -0x1.4f4a0b735c531p+3, -0x1.73a39097eadefp+1},
0234         {0x1.7acf7694f8c6p+5,   0x1.0f40ef4bed7e8p+4,  0x1.2f88d81b23f2ep+3},
0235         {0x1.87b52573912c8p+5,  0x1.3e9cadbae2c71p+4, -0x1.21c127942ffc2p-1},
0236         {0x1.95308092c646cp+5,  0x1.4bd8ec93cbe28p+4, -0x1.b1ad2fc6cd5ccp+2},
0237         {0x1.a743d71712a69p+5,  0x1.4b428f09e4704p+4, -0x1.984b766ca527bp+3},
0238         {0x1.bd35da1ada54ap+5,  0x1.3ee7b023f1a36p+4, -0x1.252f399063a3p+4},
0239         {0x1.d638af110e2bfp+5,  0x1.2a6369728426p+4,  -0x1.777a4238ecc9cp+4},
0240         {0x1.c5db1e678f93bp+5,  0x1.be23ef0d11b9fp+2,  0x1.706e52e6555aep+3},
0241         {0x1.ceab15b1e5e2cp+5,  0x1.376364145a451p+3,  0x1.d7fd2a954eedcp+1},
0242         {0x1.d8302a174d63dp+5,  0x1.67a05469bf7c1p+3, -0x1.0236bbe6ffddap+1},
0243         {0x1.e56c6a282c6fep+5,  0x1.8915eed9a5994p+3, -0x1.fb2d49a257381p+2},
0244         {0x1.f622a3ef65e7fp+5,  0x1.98638aed6156bp+3, -0x1.b57e66e36241ap+3},
0245         {0x1.04f5c4d9c25cfp+6,  0x1.9710ff57a7b7ap+3, -0x1.32004fe7bd5b5p+4},
0246         {0x1.018127d59826cp+6,  0x1.1e619caaa3a8fp-1,  0x1.a49bd0215a96dp+3},
0247         {0x1.04e57659b4cf6p+6,  0x1.8024718692a3cp+1,  0x1.addd1b874609p+2},
0248         {0x1.089bd543a0e63p+6,  0x1.2a7652607e02cp+2,  0x1.8d85013677bap+0},
0249         {0x1.0ddb424d04fadp+6,  0x1.889c9b8223651p+2, -0x1.05aa1bc86a9d4p+2},
0250         {0x1.149da8fbd8909p+6,  0x1.ce4542c130c31p+2, -0x1.3934aac98d184p+3},
0251         {0x1.1cca409e32c6dp+6,  0x1.f855300b25ba5p+2, -0x1.eac45e49f86bap+3},
0252         {0x1.23f3e081f0e6fp+6, -0x1.587d5d644e818p+2,  0x1.deea03a7e9e2p+3},
0253         {0x1.26935d2d5bb58p+6, -0x1.a6ab45ebb79ddp+1,  0x1.38265c4c6d156p+3},
0254         {0x1.29783d4927cb6p+6, -0x1.aa081e388f0b6p+0,  0x1.4de28afb99798p+2},
0255         {0x1.2d9b5abdd0df6p+6, -0x1.90d2c34c4c8p-11,  -0x1.a96c3913736p-8},
0256         {0x1.3303704c60227p+6,  0x1.7842d686da2p+0,   -0x1.6001098473213p+2},
0257         {0x1.39a911d79bf6cp+6,  0x1.51ae39272f6fp+1,  -0x1.604eff9e13224p+3},
0258         {0x1.483b66bb75f41p+6, -0x1.53e4a65e5bd0ep+3,  0x1.0dfa4f4feec89p+4},
0259         {0x1.4a4cab5bdb777p+6, -0x1.1bf0c7950b46ap+3,  0x1.9433415e2e559p+3},
0260         {0x1.4c9756fc325a6p+6, -0x1.d6e499fe26166p+2,  0x1.1693b52228314p+3},
0261         {0x1.4fe3e8502420ap+6, -0x1.6b29463056a7ep+2,  0x1.fcea504279e1bp+1},
0262         {0x1.543e4f84c51c9p+6, -0x1.01c201c3fcfbep+2, -0x1.311b349896504p+0},
0263         {0x1.59a86b698fe17p+6, -0x1.4697ac30fd276p+1, -0x1.a218614573a54p+2},
0264         {0x1.6d3f94c53849cp+6, -0x1.e750c3b6abf94p+3,  0x1.2cfd8e3a3298cp+4},
0265         {0x1.6ee9ffc7c561bp+6, -0x1.b847cb4caca9ap+3,  0x1.ea32a397a5ea3p+3},
0266         {0x1.70c3ef56a2364p+6, -0x1.8cc812a446223p+3,  0x1.7e0dc7e1aa4eep+3},
0267         {0x1.737125604c02dp+6, -0x1.5959ff3792892p+3,  0x1.ed9e6884d6db8p+2},
0268         {0x1.76fef3ccb11e4p+6, -0x1.2379a952d29eap+3,  0x1.77d044e094287p+1},
0269         {0x1.7b739668b58adp+6, -0x1.def62d62ac5d3p+2, -0x1.1242c0559f9b9p+1},
0270         {0x1.b9af6705b3e1ap+5,  0x1.3c46b4e4aa724p+4,  0x1.61ea416f62116p+3},
0271         {0x1.c34652a386648p+5,  0x1.673d5400dac6ap+4,  0x1.575529f864e4ap+1},
0272         {0x1.cd90a5a7155efp+5,  0x1.7aac661c86347p+4, -0x1.97f5d2781d1d2p+1},
0273         {0x1.dbc2183aadc6dp+5,  0x1.83f34257f81ecp+4, -0x1.24c7f27451716p+3},
0274         {0x1.ed84b050fd5fep+5,  0x1.823e0b9a9286fp+4, -0x1.dc10370a896a1p+3},
0275         {0x1.012d3f7585d8bp+6,  0x1.772916c92c8cp+4,  -0x1.444d46ad4a3f5p+4},
0276         {0x1.f498a57d50c8dp+5,  0x1.72c50647a1b01p+3,  0x1.9501b7bc92a26p+3},
0277         {0x1.fbec5add8799fp+5,  0x1.c10937ac1ff0cp+3,  0x1.71506b03a0cfcp+2},
0278         {0x1.01f4d75996fc6p+6,  0x1.f168c838b27a5p+3,  0x1.a2871b4170f08p-2},
0279         {0x1.07934868d42f1p+6,  0x1.0ba82dcee8d5fp+4, -0x1.55f50c12fdb69p+2},
0280         {0x1.0ec4c024626fep+6,  0x1.16b4701bb2cep+4,  -0x1.627c9a1b01177p+3},
0281         {0x1.1768d8b0aa031p+6,  0x1.199f4e2e9e65fp+4, -0x1.09c1c4e924702p+4},
0282         {0x1.14522a4a9a7e2p+6,  0x1.6020b0a7ecac7p+2,  0x1.c1a8f99df62d9p+3},
0283         {0x1.174b930ef061ap+6,  0x1.ec83068a9839fp+2,  0x1.06f134f7cc07ep+3},
0284         {0x1.1a90921a38fe7p+6,  0x1.28d89eb448a2cp+3,  0x1.ab5fe99a94265p+1},
0285         {0x1.1f36b9520ad75p+6,  0x1.57ace8397fb96p+3, -0x1.0feb2da7758d4p+1},
0286         {0x1.253f2b741784fp+6,  0x1.7c087d08384b1p+3, -0x1.efdb49212f992p+2},
0287         {0x1.2c9a998952cf1p+6,  0x1.9366b5a4edd8ap+3, -0x1.a9848b6099745p+3},
0288         {0x1.330541ae3e385p+6, -0x1.39e238ea7801p-1,   0x1.f5ec9f058d778p+3},
0289         {0x1.3568ff8940412p+6,  0x1.4b2aea6a2b17bp+0,  0x1.5aadb9534e15p+3},
0290         {0x1.380d495b09ffp+6,   0x1.6b2260c162acep+1,  0x1.a0b3e40fdedfp+2},
0291         {0x1.3bd6db55a86abp+6,  0x1.1c0894632c07ep+2,  0x1.73acfbc73a52cp+0},
0292         {0x1.40ced5740b6ecp+6,  0x1.784d8d5bd59bap+2, -0x1.f87f29ae0432ap+1},
0293         {0x1.46f1d56d6a627p+6,  0x1.c2acec311d85p+2,  -0x1.2d078d1d0ec93p+3},
0294         {0x1.54692a261ff77p+6, -0x1.91aafde5841e2p+2,  0x1.1733b7c5acf09p+4},
0295         {0x1.5657fb0185f53p+6, -0x1.292fef1c6094ap+2,  0x1.adacb9827ba1ep+3},
0296         {0x1.587cf8c168362p+6, -0x1.9aa045411de21p+1,  0x1.34f47fa15abacp+3},
0297         {0x1.5b946ca219efdp+6, -0x1.98465c0dc7b2ap+0,  0x1.43ded9d06d2f2p+2},
0298         {0x1.5faadb5249c01p+6, -0x1.d35a0bdd6ap-11,   -0x1.f008c176226p-8},
0299         {0x1.64c3b31613c93p+6,  0x1.6ffd305ac700dp+0, -0x1.52580b551209ep+2},
0300         {0x1.773c645e32d2dp+6, -0x1.6c001f7fba478p+3,  0x1.3482f283026fp+4},
0301         {0x1.78d215b19332bp+6, -0x1.3f6c1cce8d703p+3,  0x1.fda06ae8dbf53p+3},
0302         {0x1.7a9531dc8addap+6, -0x1.15dd42d16c7e4p+3,  0x1.94e8098e96fccp+3},
0303         {0x1.7d21e2193d5bcp+6, -0x1.c8c718caa9f8ep+2,  0x1.1105d830d79dp+3},
0304         {0x1.808572bc9578ap+6, -0x1.607bf910c57eep+2,  0x1.ebe2b93545493p+1},
0305         {0x1.84c6a7914070fp+6, -0x1.f6c2ba3dfc25ap+1, -0x1.2beab2de5e36ep+0},
0306         {0x1.f663baac570efp+5,  0x1.67be9f690c994p+4,  0x1.928e76c3750aep+3},
0307         {0x1.fdd78bfa0c16bp+5,  0x1.8da11d29326eap+4,  0x1.63128e5e5d16fp+2},
0308         {0x1.02fa8211ca545p+6,  0x1.a3a4c44447321p+4,  0x1.04d0a318f0ebp-3},
0309         {0x1.08ace6a6b1bbcp+6,  0x1.b32f97fdc6c72p+4, -0x1.69ea7f28a916ap+2},
0310         {0x1.0ff3cb8943e81p+6,  0x1.b9bf03db71bbbp+4, -0x1.6cb54bc7e9c6p+3},
0311         {0x1.18ac5c68852cdp+6,  0x1.b753a9bd5dbeep+4, -0x1.0ebf0dff35bfbp+4},
0312         {0x1.12e57190e4906p+6,  0x1.f7243f8456bc9p+3,  0x1.bbbf019d6f15fp+3},
0313         {0x1.15f567ae01685p+6,  0x1.1e238872ebfe1p+4,  0x1.f5db81101607bp+2},
0314         {0x1.1952176f81196p+6,  0x1.35eb43e1b3454p+4,  0x1.6f044118da651p+1},
0315         {0x1.1e17b3f5be1e9p+6,  0x1.4aa233635e996p+4, -0x1.54536023af51fp+1},
0316         {0x1.244540fde90c8p+6,  0x1.590428a4cd957p+4, -0x1.09fd595c81312p+3},
0317         {0x1.2bc8d270727cdp+6,  0x1.6002d76c0fae1p+4, -0x1.bbbb8dd0769e1p+3},
0318         {0x1.2910051d7f4b8p+6,  0x1.4505f3d8c93bep+3,  0x1.e1f0441f64aep+3},
0319         {0x1.2ba848891bba1p+6,  0x1.83d80a13be75dp+3,  0x1.39f759ee811ebp+3},
0320         {0x1.2e852380cafc2p+6,  0x1.b3d439de20201p+3,  0x1.5018eb1694a48p+2},
0321         {0x1.329c951f395aap+6,  0x1.e28db45c54996p+3,  0x1.689e197414bp-7},
0322         {0x1.37f5013da44b7p+6,  0x1.04846845ce376p+4, -0x1.5f9623aa14c25p+2},
0323         {0x1.3e86c9cea44c3p+6,  0x1.11f59319394fdp+4, -0x1.6049926fe33dap+3},
0324         {0x1.443efb939f1d1p+6,  0x1.0a131d35fab88p+2,  0x1.0838884c6c35ap+4},
0325         {0x1.4667729b66dbap+6,  0x1.793a281884d1p+2,   0x1.8155ad5254ba1p+3},
0326         {0x1.48cb51f255527p+6,  0x1.d609d14c020d1p+2,  0x1.fd82e0735fdcep+2},
0327         {0x1.4c3af36c4bb13p+6,  0x1.1c709781a9fb2p+3,  0x1.8ca66dc4e33fdp+1},
0328         {0x1.50c14ed0ba88bp+6,  0x1.4a1d653379591p+3, -0x1.14ee52d3fd0e1p+1},
0329         {0x1.565e87fd89a8p+6,   0x1.6fe5c4c37980fp+3, -0x1.e3e3e3d81f648p+2},
0330         {0x1.62b687ada6a2dp+6, -0x1.b1a7c98c53b9bp+0,  0x1.221d2fc2be6cp+4},
0331         {0x1.6480f342fedfap+6, -0x1.728e49c5a9ba8p-3,  0x1.cb44060a17de8p+3},
0332         {0x1.667e081a8e931p+6,  0x1.2c204353d8acp+0,   0x1.582d07a1ac706p+3},
0333         {0x1.695d03a4b4ea6p+6,  0x1.5b9dd1e5192c9p+1,  0x1.949cc8da7db3dp+2},
0334         {0x1.6d2ad25b49139p+6,  0x1.10c661536b1e3p+2,  0x1.60b9aee667ea2p+0},
0335         {0x1.71ec510363cccp+6,  0x1.6b2af260ca3f1p+2, -0x1.e8e3d6769ed61p+1},
0336         {0x1.83323c984ee7ap+6, -0x1.c8df58716d4cbp+2,  0x1.3d9335b5d20f9p+4},
0337         {0x1.84b109344c92cp+6, -0x1.750134f94569cp+2,  0x1.0a63166d3ca32p+4},
0338         {0x1.865ae5967696dp+6, -0x1.260f129da839ep+2,  0x1.b00f6d0ac1f93p+3},
0339         {0x1.88c3891dbffdp+6,  -0x1.8e7bf5d542ea4p+1,  0x1.30323669c3d67p+3},
0340         {0x1.8bf825267af8ep+6, -0x1.89d8c0f2ba1ffp+0,  0x1.3b2af0c9eed76p+2},
0341         {0x1.8ffffbb77c76ap+6, -0x1.09cab717214p-10,  -0x1.1a1aa7765a7p-7},
0342         {0x1.ae1c063cf8075p+3, -0x1.1dcc8d6b21p-13,   -0x1.2f56d49352fp-10},
0343         {0x1.23869fde6955fp+4, -0x1.836d13c82dp-13,   -0x1.9b340cb2926p-10},
0344         {0x1.6a51d9755cb1ep+4, -0x1.e1821bf6d08p-13,  -0x1.ff0f3c5806ap-10},
0345         {0x1.adca073d0c9a1p+4, -0x1.1d961152df8p-12,  -0x1.2f1d00745cap-9},
0346         {0x1.eeb26a3638306p+4, -0x1.48b751f0a58p-12,  -0x1.5ce3e1a3db8p-9},
0347         {0x1.16c4868a9dbc4p+5, -0x1.7278906708p-12,   -0x1.89352628678p-9},
0348         {0x1.3552cb4726ed2p+5, -0x1.9b140ac6fbp-12,   -0x1.b44e9f2325p-9},
0349         {0x1.53242132979b2p+5, -0x1.c2b46f9c328p-12,  -0x1.de5d99b6f9p-9},
0350         {0x1.7051013cf5a69p+5, -0x1.e97a44c10a8p-12,  -0x1.03c24d5b897p-8},
0351         {0x1.8ceca3a0569fdp+5, -0x1.07bf8a7e0fcp-11,  -0x1.17ef5f1c441p-8},
0352         {0x1.a9067ed63306p+5,  -0x1.1a6bb67a6bp-11,   -0x1.2bc0e9df0a8p-8},
0353         {0x1.c4ab42f91535bp+5, -0x1.2cca14a233p-11,   -0x1.3f3fe0662d2p-8},
0354         {0x1.dfe5855ae1528p+5, -0x1.3ee1ad40618p-11,  -0x1.5273b9784a3p-8},
0355         {0x1.fabe397d15c9dp+5, -0x1.50b872fc158p-11,  -0x1.6562c531248p-8},
0356         {0x1.0a9e8459f8d9ap+6, -0x1.62537d1958p-11,   -0x1.78126ad030ep-8},
0357         {0x1.17b44990f13f5p+6, -0x1.73b732a42fp-11,   -0x1.8a87568ccdp-8},
0358         {0x1.24a350705ddf4p+6, -0x1.84e76b1c308p-11,  -0x1.9cc59c424aap-8},
0359         {0x1.316e23ed9d96ap+6, -0x1.95e7879947p-11,   -0x1.aed0d2216dp-8},
0360         {0x1.3e1704b29c069p+6, -0x1.a6ba8679e08p-11,  -0x1.c0ac258f992p-8},
0361         {0x1.4a9ff4d8984e6p+6, -0x1.b76312f3808p-11,  -0x1.d25a6bb27acp-8},
0362         {0x1.570ac151c1615p+6, -0x1.c7e3919c138p-11,  -0x1.e3de2eb684p-8},
0363         {0x1.6359098c40c61p+6, -0x1.d83e2a89bbp-11,   -0x1.f539b894d66p-8},
0364         {0x1.6f8c45b456692p+6, -0x1.e874d1a7f5p-11,   -0x1.03378df3804p-7},
0365         {0x1.7ba5cbe12c3fcp+6, -0x1.f8894d9602p-11,   -0x1.0bc01d99c93p-7},
0366     };
0367 
0368     // Perform the inverse gamma companding for a sRGB color
0369     // http://www.brucelindbloom.com/index.html?Eqn_RGB_to_XYZ.html
0370     // double inverseGammaCompanding(int c) {
0371     //     // [0, 255] to [0, 1]
0372     //     double v = c / 255.0;
0373     //     if (v <= 0.04045) {
0374     //         return v / 12.92;
0375     //     }
0376     //     return std::pow((v + 0.055) / 1.055, 2.4);
0377     // };
0378     constexpr double RGB888_to_sRGB_table[] {
0379         0x0p+0,                0x1.3e45677c176f7p-12, 0x1.3e45677c176f7p-11, 0x1.dd681b3a23272p-11,
0380         0x1.3e45677c176f7p-10, 0x1.8dd6c15b1d4b4p-10, 0x1.dd681b3a23272p-10, 0x1.167cba8c94818p-9,
0381         0x1.3e45677c176f7p-9,  0x1.660e146b9a5d6p-9,  0x1.8dd6c15b1d4b4p-9,  0x1.b6a31b5259c99p-9,
0382         0x1.e1e31d70c99ddp-9,  0x1.07c38bf8583a9p-8,  0x1.1fcc2beed6421p-8,  0x1.390ffaf95e279p-8,
0383         0x1.53936cc7bc928p-8,  0x1.6f5addb50c915p-8,  0x1.8c6a94031b561p-8,  0x1.aac6c0fb97351p-8,
0384         0x1.ca7381f9f602bp-8,  0x1.eb74e160978dp-8,   0x1.06e76bbda92b8p-7,  0x1.18c2a5a8a8044p-7,
0385         0x1.2b4e09b3f0ae3p-7,  0x1.3e8b7b3bde965p-7,  0x1.527cd60af8b85p-7,  0x1.6723eea8d3709p-7,
0386         0x1.7c8292a3db6b3p-7,  0x1.929a88d67b521p-7,  0x1.a96d91a8016bdp-7,  0x1.c0fd67499fab6p-7,
0387         0x1.d94bbdefd740ep-7,  0x1.f25a44089883fp-7,  0x1.061551372c694p-6,  0x1.135f3e4c2cce2p-6,
0388         0x1.210bb8642b172p-6,  0x1.2f1b8c1ae46bdp-6,  0x1.3d8f839b79c0bp-6,  0x1.4c6866b3e9fa4p-6,
0389         0x1.5ba6fae794313p-6,  0x1.6b4c0380d2deep-6,  0x1.7b5841a1bf3acp-6,  0x1.8bcc74542addbp-6,
0390         0x1.9ca95898dc8b5p-6,  0x1.adefa9761c02p-6,   0x1.bfa0200597bd9p-6,  0x1.d1bb7381aec1fp-6,
0391         0x1.e442595227bcap-6,  0x1.f73585185e1b5p-6,  0x1.054ad45d76878p-5,  0x1.0f31ba386ff26p-5,
0392         0x1.194fcb663747bp-5,  0x1.23a55e62a662ap-5,  0x1.2e32c8e148d11p-5,  0x1.38f85fd21eacfp-5,
0393         0x1.43f67766310ffp-5,  0x1.4f2d6313fa8dp-5,   0x1.5a9d759ba5edp-5,   0x1.6647010b254eep-5,
0394         0x1.722a56c2239eep-5,  0x1.7e47c775d2427p-5,  0x1.8a9fa33494b07p-5,  0x1.973239698b9ccp-5,
0395         0x1.a3ffd8e001389p-5,  0x1.b108cfc6b7fbcp-5,  0x1.be4d6bb31d522p-5,  0x1.cbcdf9a4616f2p-5,
0396         0x1.d98ac60675833p-5,  0x1.e7841cb4f16dfp-5,  0x1.f5ba48fde2048p-5,  0x1.0216cad240765p-4,
0397         0x1.096f2671eb815p-4,  0x1.10e65c38a5192p-4,  0x1.187c90bf8bce2p-4,  0x1.2031e85f5d6dap-4,
0398         0x1.28068731a1952p-4,  0x1.2ffa9111cb94bp-4,  0x1.380e299e53f92p-4,  0x1.40417439ca10fp-4,
0399         0x1.4894940bddbfbp-4,  0x1.5107ac0261e59p-4,  0x1.599aded247aacp-4,  0x1.624e4ef892ed4p-4,
0400         0x1.6b221ebb4817ep-4,  0x1.7416702a539d1p-4,  0x1.7d2b65206b527p-4,  0x1.86611f43e9e6ap-4,
0401         0x1.8fb7c007a4a7p-4,   0x1.992f68abbbc89p-4,  0x1.a2c83a3e6566dp-4,  0x1.ac82559cb3644p-4,
0402         0x1.b65ddb7354604p-4,  0x1.c05aec3f4fe5ep-4,  0x1.ca79a84ebe03p-4,   0x1.d4ba2fc17a6a5p-4,
0403         0x1.df1ca289d34b8p-4,  0x1.e9a1206d34003p-4,  0x1.f447c904cbb4ep-4,  0x1.ff10bbbe302c2p-4,
0404         0x1.04fe0bedfe5f1p-3,  0x1.0a84fe3b36d8fp-3,  0x1.101d443dfc06fp-3,  0x1.15c6ed58eefdfp-3,
0405         0x1.1b8208da5fefp-3,   0x1.214ea5fc9514ap-3,  0x1.272cd3e610123p-3,  0x1.2d1ca1a9d1cfbp-3,
0406         0x1.331e1e479cdf5p-3,  0x1.393158ac3674ep-3,  0x1.3f565fb1a5fd5p-3,  0x1.458d421f735dfp-3,
0407         0x1.4bd60eaae3e73p-3,  0x1.5230d3f736034p-3,  0x1.589da095dbaa1p-3,  0x1.5f1c8306b3a3cp-3,
0408         0x1.65ad89b841a2bp-3,  0x1.6c50c307e53bfp-3,  0x1.73063d420fc8p-3,   0x1.79ce06a279303p-3,
0409         0x1.80a82d5453b5dp-3,  0x1.8794bf727eb3fp-3,  0x1.8e93cb07b8679p-3,  0x1.95a55e0ecec0bp-3,
0410         0x1.9cc98672cf47ep-3,  0x1.a400520f3619cp-3,  0x1.ab49ceb01c003p-3,  0x1.b2a60a1263b0ap-3,
0411         0x1.ba1511e3e632dp-3,  0x1.c196f3c39e76fp-3,  0x1.c92bbd41d41fep-3,  0x1.d0d37be045851p-3,
0412         0x1.d88e3d1250f68p-3,  0x1.e05c0e3d1d3ep-3,   0x1.e83cfcb7c16fp-3,   0x1.f03115cb6bfd3p-3,
0413         0x1.f83866b38924dp-3,  0x1.00297e4ef4553p-2,  0x1.044072557177ap-2,  0x1.086115f6beb3ap-2,
0414         0x1.0c8b6fb5c735ep-2,  0x1.10bf860ef039ap-2,  0x1.14fd5f782a5a6p-2,  0x1.1945026102997p-2,
0415         0x1.1d967532b31b1p-2,  0x1.21f1be50339e7p-2,  0x1.2656e41649ae3p-2,  0x1.2ac5ecdb988f8p-2,
0416         0x1.2f3edef0b0ed8p-2,  0x1.33c1c0a020438p-2,  0x1.384e982e800b1p-2,  0x1.3ce56bda84a81p-2,
0417         0x1.418641dd0c1bcp-2,  0x1.463120692c7afp-2,  0x1.4ae60dac4229dp-2,  0x1.4fa50fcdfde15p-2,
0418         0x1.546e2cf0727a9p-2,  0x1.59416b3022858p-2,  0x1.5e1ed0a40daabp-2,  0x1.6306635dbdd7bp-2,
0419         0x1.67f82969543a2p-2,  0x1.6cf428cd96079p-2,  0x1.71fa678bf915dp-2,  0x1.770aeba0b042ap-2,
0420         0x1.7c25bb02b7ac5p-2,  0x1.814adba3e0bd9p-2,  0x1.867a5370de0b1p-2,  0x1.8bb428514f067p-2,
0421         0x1.90f86027cb84ep-2,  0x1.964700d1ef1b1p-2,  0x1.9ba0102864521p-2,  0x1.a10393feefafdp-2,
0422         0x1.a67192247a9bep-2,  0x1.abea10631e195p-2,  0x1.b16d14802d5cap-2,  0x1.b6faa43c403bbp-2,
0423         0x1.bc92c5533d785p-2,  0x1.c2357d7c64e5dp-2,  0x1.c7e2d26a596dep-2,  0x1.cd9ac9cb2aef2p-2,
0424         0x1.d35d69485ffc5p-2,  0x1.d92ab686ff782p-2,  0x1.df02b7279a10dp-2,  0x1.e4e570c6539c5p-2,
0425         0x1.ead2e8faec526p-2,  0x1.f0cb2558c9ea4p-2,  0x1.f6ce2b6f00983p-2,  0x1.fcdc00c85bec2p-2,
0426         0x1.017a5575b3cb2p-1,  0x1.048c17ad3c04bp-1,  0x1.07a349c9d9837p-1,  0x1.0abfee888c05p-1,
0427         0x1.0de208a4444c8p-1,  0x1.11099ad5e83ebp-1,  0x1.1436a7d456eefp-1,  0x1.176932546ca12p-1,
0428         0x1.1aa13d0906bdap-1,  0x1.1ddecaa307b85p-1,  0x1.2121ddd15aecep-1,  0x1.246a7940f86d1p-1,
0429         0x1.27b89f9ce8c4bp-1,  0x1.2b0c538e48b07p-1,  0x1.2e6597bc4ccap-1,   0x1.31c46ecc4528dp-1,
0430         0x1.3528db61a0f73p-1,  0x1.3892e01df1fccp-1,  0x1.3c027fa0f01ebp-1,  0x1.3f77bc887cd3bp-1,
0431         0x1.42f29970a68f8p-1,  0x1.467318f3ac22dp-1,  0x1.49f93daa00113p-1,  0x1.4d850a2a4bde1p-1,
0432         0x1.51168109734e5p-1,  0x1.54ada4da97a1bp-1,  0x1.584a782f1ac23p-1,  0x1.5becfd96a2698p-1,
0433         0x1.5f95379f1b3edp-1,  0x1.634328d4bbe97p-1,  0x1.66f6d3c2081cfp-1,  0x1.6ab03aefd39aap-1,
0434         0x1.6e6f60e5452b1p-1,  0x1.72344827d98f6p-1,  0x1.75fef33b6669bp-1,  0x1.79cf64a21d1e2p-1,
0435         0x1.7da59edc8dabp-1,   0x1.8181a469a9787p-1,  0x1.856377c6c6224p-1,  0x1.894b1b6fa0377p-1,
0436         0x1.8d3891de5df49p-1,  0x1.912bdd8b91f45p-1,  0x1.952500ee3dda5p-1,  0x1.9923fe7bd4f67p-1,
0437         0x1.9d28d8a83edfcp-1,  0x1.a13391e5da09fp-1,  0x1.a5442ca57e52ep-1,  0x1.a95aab567f88fp-1,
0438         0x1.ad771066afec2p-1,  0x1.b1995e4262a69p-1,  0x1.b5c197546e3f8p-1,  0x1.b9efbe062f086p-1,
0439         0x1.be23d4bf8981bp-1,  0x1.c25ddde6ecbbbp-1,  0x1.c69ddbe154af1p-1,  0x1.cae3d1124c90bp-1,
0440         0x1.cf2fbfdbf11f1p-1,  0x1.d381aa9ef2e82p-1,  0x1.d7d993ba988d4p-1,  0x1.dc377d8cc0fd5p-1,
0441         0x1.e09b6a71e5aa6p-1,  0x1.e5055cc51cbb4p-1,  0x1.e97556e01b351p-1,  0x1.edeb5b1b37216p-1,
0442         0x1.f2676bcd69adep-1,  0x1.f6e98b4c51466p-1,  0x1.fb71bbec33ab2p-1,  0x1p+0,
0443     };
0444 // clang-format on
0445 
0446 // convert a RGB color to Oklab (https://bottosson.github.io/posts/oklab/)
0447 Lab rgbToOklab(QRgb rgb)
0448 {
0449     const double r = RGB888_to_sRGB_table[qRed(rgb)];
0450     const double g = RGB888_to_sRGB_table[qGreen(rgb)];
0451     const double b = RGB888_to_sRGB_table[qBlue(rgb)];
0452 
0453     const double l = std::cbrt(0.4122214708 * r + 0.5363325363 * g + 0.0514459929 * b);
0454     const double m = std::cbrt(0.2119034982 * r + 0.6806995451 * g + 0.1073969566 * b);
0455     const double s = std::cbrt(0.0883024619 * r + 0.2817188376 * g + 0.6299787005 * b);
0456 
0457     // M2 * 100 * {l', m', s'}
0458     return Lab{
0459         (021.04542553 * l + 079.36177850 * m - 000.40720468 * s),
0460         (197.79984951 * l - 242.85922050 * m + 045.05937099 * s),
0461         (002.59040371 * l + 078.27717662 * m - 080.86757660 * s),
0462     };
0463 }
0464 
0465 constexpr double epsilon = 1e-15;
0466 
0467 inline double sinDegree(double x)
0468 {
0469     return std::sin(x * M_PI / 180.0);
0470 }
0471 
0472 inline double cosDegree(double x)
0473 {
0474     return std::cos(x * M_PI / 180.0);
0475 }
0476 
0477 inline double pow2(double x)
0478 {
0479     return x * x;
0480 }
0481 
0482 inline double computeHPrime(double a_prime, double b)
0483 {
0484     if (std::abs(a_prime) < epsilon && std::abs(b) < epsilon) {
0485         return 0.0;
0486     }
0487 
0488     const double value = std::atan2(b, a_prime) * 180.0 / M_PI;
0489     return (value < 0.0) ? value + 360.0 : value;
0490 }
0491 
0492 inline double computeDeltaHPrime(double C1_prime, double C2_prime, double h1_prime, double h2_prime)
0493 {
0494     if (C1_prime * C2_prime < epsilon) {
0495         return 0.0;
0496     }
0497 
0498     const double diff = h2_prime - h1_prime;
0499 
0500     if (std::abs(diff) <= 180.0) {
0501         return diff;
0502     } else if (diff > 180.0) {
0503         return diff - 360.0;
0504     } else {
0505         return diff + 360.0;
0506     }
0507 }
0508 
0509 inline double computeHPrimeBar(double C1_prime, double C2_prime, double h1_prime, double h2_prime)
0510 {
0511     const double sum = h1_prime + h2_prime;
0512 
0513     if (C1_prime * C2_prime < epsilon) {
0514         return sum;
0515     }
0516 
0517     const double dist = std::abs(h1_prime - h2_prime);
0518 
0519     if (dist <= 180.0) {
0520         return 0.5 * sum;
0521     } else if (sum < 360.0) {
0522         return 0.5 * (sum + 360.0);
0523     } else {
0524         return 0.5 * (sum - 360.0);
0525     }
0526 }
0527 
0528 /// Calculate the perceptual color difference based on CIEDE2000.
0529 /// https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
0530 /// \return The color difference of the two colors.
0531 double calculate_CIEDE2000(const Lab &color1, const Lab &color2)
0532 {
0533     const double L1 = color1.L;
0534     const double a1 = color1.a;
0535     const double b1 = color1.b;
0536     const double L2 = color2.L;
0537     const double a2 = color2.a;
0538     const double b2 = color2.b;
0539 
0540     const double _25_pow_7 = /*std::pow(25.0, 7.0) = */ 6103515625.0;
0541 
0542     const double C1_ab = std::sqrt(a1 * a1 + b1 * b1);
0543     const double C2_ab = std::sqrt(a2 * a2 + b2 * b2);
0544     const double C_ab_bar = 0.5 * (C1_ab + C2_ab);
0545     const double c_ab_bar_pow_7 = std::pow(C_ab_bar, 7.0);
0546     const double G = 0.5 * (1.0 - std::sqrt(c_ab_bar_pow_7 / (c_ab_bar_pow_7 + _25_pow_7)));
0547     const double a1_prime = (1.0 + G) * a1;
0548     const double a2_prime = (1.0 + G) * a2;
0549     const double C1_prime = std::sqrt(a1_prime * a1_prime + b1 * b1);
0550     const double C2_prime = std::sqrt(a2_prime * a2_prime + b2 * b2);
0551     const double h1_prime = computeHPrime(a1_prime, b1);
0552     const double h2_prime = computeHPrime(a2_prime, b2);
0553 
0554     const double deltaL_prime = L2 - L1;
0555     const double deltaC_prime = C2_prime - C1_prime;
0556     const double deltah_prime = computeDeltaHPrime(C1_prime, C2_prime, h1_prime, h2_prime);
0557     const double deltaH_prime = 2.0 * std::sqrt(C1_prime * C2_prime) * sinDegree(0.5 * deltah_prime);
0558 
0559     const double L_primeBar = 0.5 * (L1 + L2);
0560     const double C_primeBar = 0.5 * (C1_prime + C2_prime);
0561     const double h_primeBar = computeHPrimeBar(C1_prime, C2_prime, h1_prime, h2_prime);
0562 
0563     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)
0564         - 0.20 * cosDegree(4.0 * h_primeBar - 63.0);
0565 
0566     const double C_primeBar_pow7 = std::pow(C_primeBar, 7.0);
0567     const double R_C = 2.0 * std::sqrt(C_primeBar_pow7 / (C_primeBar_pow7 + _25_pow_7));
0568     const double S_L = 1.0 + (0.015 * pow2(L_primeBar - 50.0)) / std::sqrt(20.0 + pow2(L_primeBar - 50.0));
0569     const double S_C = 1.0 + 0.045 * C_primeBar;
0570     const double S_H = 1.0 + 0.015 * C_primeBar * T;
0571     const double R_T = -R_C * sinDegree(60.0 * std::exp(-pow2((h_primeBar - 275) / 25.0)));
0572 
0573     constexpr double kL = 1.0;
0574     constexpr double kC = 1.0;
0575     constexpr double kH = 1.0;
0576 
0577     const double deltaL = deltaL_prime / (kL * S_L);
0578     const double deltaC = deltaC_prime / (kC * S_C);
0579     const double deltaH = deltaH_prime / (kH * S_H);
0580 
0581     return /*std::sqrt*/ (deltaL * deltaL + deltaC * deltaC + deltaH * deltaH + R_T * deltaC * deltaH);
0582 }
0583 
0584 struct AnsiBuffer {
0585     using ColorCache = QHash<QRgb, int>;
0586 
0587     void append(char c)
0588     {
0589         Q_ASSERT(remaining() >= 1);
0590         m_data[m_size] = c;
0591         ++m_size;
0592     }
0593 
0594     void append(QLatin1String str)
0595     {
0596         Q_ASSERT(remaining() >= str.size());
0597         memcpy(m_data + m_size, str.data(), str.size());
0598         m_size += str.size();
0599     }
0600 
0601     void appendForeground(QRgb rgb, bool is256Colors, ColorCache &colorCache)
0602     {
0603         append(QLatin1String("38;"));
0604         append(rgb, is256Colors, colorCache);
0605     }
0606 
0607     void appendBackground(QRgb rgb, bool is256Colors, ColorCache &colorCache)
0608     {
0609         append(QLatin1String("48;"));
0610         append(rgb, is256Colors, colorCache);
0611     }
0612 
0613     void append(QRgb rgb, bool is256Colors, ColorCache &colorCache)
0614     {
0615         auto appendUInt8 = [&](int x) {
0616             Q_ASSERT(x <= 255 && x >= 0);
0617             if (x > 99) {
0618                 if (x >= 200) {
0619                     append('2');
0620                     x -= 200;
0621                 } else {
0622                     append('1');
0623                     x -= 100;
0624                 }
0625             } else if (x < 10) {
0626                 append(char('0' + x));
0627                 return;
0628             }
0629 
0630             // clang-format off
0631             constexpr char const* tb2digits =
0632                 "00" "01" "02" "03" "04" "05" "06" "07" "08" "09"
0633                 "10" "11" "12" "13" "14" "15" "16" "17" "18" "19"
0634                 "20" "21" "22" "23" "24" "25" "26" "27" "28" "29"
0635                 "30" "31" "32" "33" "34" "35" "36" "37" "38" "39"
0636                 "40" "41" "42" "43" "44" "45" "46" "47" "48" "49"
0637                 "50" "51" "52" "53" "54" "55" "56" "57" "58" "59"
0638                 "60" "61" "62" "63" "64" "65" "66" "67" "68" "69"
0639                 "70" "71" "72" "73" "74" "75" "76" "77" "78" "79"
0640                 "80" "81" "82" "83" "84" "85" "86" "87" "88" "89"
0641                 "90" "91" "92" "93" "94" "95" "96" "97" "98" "99";
0642             // clang-format on
0643 
0644             auto *p = tb2digits + x * 2;
0645             append(p[0]);
0646             append(p[1]);
0647         };
0648 
0649         if (is256Colors) {
0650             double dist = 1e24;
0651             int idx = 0;
0652             auto it = colorCache.find(rgb);
0653             if (it == colorCache.end()) {
0654                 const auto lab = rgbToOklab(rgb);
0655                 // find the nearest xterm color
0656                 for (Lab const &xtermLab : xterm240_Oklabs) {
0657                     auto dist2 = calculate_CIEDE2000(lab, xtermLab);
0658                     if (dist2 < dist) {
0659                         dist = dist2;
0660                         idx = &xtermLab - xterm240_Oklabs;
0661                     }
0662                 }
0663                 // add 16 to convert 240 colors mode to 256 colors mode
0664                 idx += 16;
0665                 colorCache.insert(rgb, idx);
0666             } else {
0667                 idx = it.value();
0668             }
0669 
0670             append('5');
0671             append(';');
0672             appendUInt8(idx);
0673         } else {
0674             append('2');
0675             append(';');
0676             appendUInt8(qRed(rgb));
0677             append(';');
0678             appendUInt8(qGreen(rgb));
0679             append(';');
0680             appendUInt8(qBlue(rgb));
0681         }
0682         append(';');
0683     }
0684 
0685     // Replace last character with 'm'. Last character must be ';'
0686     void setFinalStyle()
0687     {
0688         Q_ASSERT(m_data[m_size - 1] == ';');
0689         m_data[m_size - 1] = 'm';
0690     }
0691 
0692     void clear()
0693     {
0694         m_size = 0;
0695     }
0696 
0697     QLatin1String latin1() const
0698     {
0699         return QLatin1String(m_data, m_size);
0700     }
0701 
0702 private:
0703     char m_data[128];
0704     int m_size = 0;
0705 
0706     int remaining() const noexcept
0707     {
0708         return 128 - m_size;
0709     }
0710 };
0711 
0712 void fillString(QString &s, int n, QStringView fill)
0713 {
0714     if (n > 0) {
0715         for (; n > fill.size(); n -= fill.size()) {
0716             s += fill;
0717         }
0718         s += fill.left(n);
0719     }
0720 }
0721 
0722 struct GraphLine {
0723     QString graphLine;
0724     QString labelLine;
0725     int graphLineLength = 0;
0726     int labelLineLength = 0;
0727     int nextLabelOffset = 0;
0728 
0729     template<class String>
0730     void pushLabel(int offset, String const &s, int numberDisplayableChar)
0731     {
0732         Q_ASSERT(offset >= labelLineLength);
0733         const int n = offset - labelLineLength;
0734         labelLineLength += numberDisplayableChar + n;
0735         fillLine(labelLine, n);
0736         labelLine += s;
0737         nextLabelOffset = labelLineLength;
0738     }
0739 
0740     template<class String>
0741     void pushGraph(int offset, String const &s, int numberDisplayableChar)
0742     {
0743         Q_ASSERT(offset >= graphLineLength);
0744         const int n = offset - graphLineLength;
0745         graphLineLength += numberDisplayableChar + n;
0746         fillLine(graphLine, n);
0747         const int ps1 = graphLine.size();
0748         graphLine += s;
0749         if (offset >= labelLineLength) {
0750             const int n2 = offset - labelLineLength;
0751             labelLineLength += n2 + 1;
0752             fillLine(labelLine, n2);
0753             labelLine += QStringView(graphLine).right(graphLine.size() - ps1);
0754         }
0755     }
0756 
0757 private:
0758     static void fillLine(QString &s, int n)
0759     {
0760         Q_ASSERT(n >= 0);
0761         fillString(s,
0762                    n,
0763                    QStringLiteral("                              "
0764                                   "                              "
0765                                   "                              "));
0766     }
0767 };
0768 
0769 /**
0770  * Returns the first free line at a given position or create a new one
0771  */
0772 GraphLine &lineAtOffset(std::vector<GraphLine> &graphLines, int offset)
0773 {
0774     const auto last = graphLines.end();
0775     auto p = std::find_if(graphLines.begin(), last, [=](GraphLine const &line) {
0776         return line.nextLabelOffset < offset;
0777     });
0778     if (p == last) {
0779         graphLines.emplace_back();
0780         return graphLines.back();
0781     }
0782     return *p;
0783 }
0784 
0785 // disable bold, italic and underline on |
0786 const QLatin1String graphSymbol("\x1b[21;23;24m|");
0787 // reverse video
0788 const QLatin1String nameStyle("\x1b[7m");
0789 
0790 /**
0791  * ANSI Highlighter dedicated to traces
0792  */
0793 class DebugSyntaxHighlighter : public KSyntaxHighlighting::AbstractHighlighter
0794 {
0795 public:
0796     using TraceOption = KSyntaxHighlighting::AnsiHighlighter::TraceOption;
0797     using TraceOptions = KSyntaxHighlighting::AnsiHighlighter::TraceOptions;
0798 
0799     void setDefinition(const KSyntaxHighlighting::Definition &def) override
0800     {
0801         AbstractHighlighter::setDefinition(def);
0802         m_contextCapture.setDefinition(def);
0803 
0804         const auto &definitions = def.includedDefinitions();
0805         for (const auto &definition : definitions) {
0806             const auto *defData = DefinitionData::get(definition);
0807             for (const auto &context : defData->contexts) {
0808                 m_defDataBycontexts.insert(&context, defData);
0809             }
0810         }
0811     }
0812 
0813     void highlightData(QTextStream &in,
0814                        QTextStream &out,
0815                        QLatin1String infoStyle,
0816                        QLatin1String editorBackground,
0817                        const std::vector<QPair<QString, QString>> &ansiStyles,
0818                        TraceOptions traceOptions)
0819     {
0820         initRegionStyles(ansiStyles);
0821 
0822         m_hasFormatTrace = traceOptions.testFlag(TraceOption::Format);
0823         m_hasRegionTrace = traceOptions.testFlag(TraceOption::Region);
0824         m_hasStackSizeTrace = traceOptions.testFlag(TraceOption::StackSize);
0825         m_hasContextTrace = traceOptions.testFlag(TraceOption::Context);
0826         const bool hasFormatOrContextTrace = m_hasFormatTrace || m_hasContextTrace || m_hasStackSizeTrace;
0827 
0828         const bool hasSeparator = hasFormatOrContextTrace && m_hasRegionTrace;
0829         const QString resetBgColor = (editorBackground.isEmpty() ? QStringLiteral("\x1b[0m") : editorBackground);
0830 
0831         bool firstLine = true;
0832         State state;
0833         QString currentLine;
0834         while (in.readLineInto(&currentLine)) {
0835             auto oldState = state;
0836             state = highlightLine(currentLine, state);
0837 
0838             if (hasSeparator) {
0839                 if (!firstLine) {
0840                     out << QStringLiteral("\x1b[0m────────────────────────────────────────────────────\x1b[K\n");
0841                 }
0842                 firstLine = false;
0843             }
0844 
0845             if (!m_regions.empty()) {
0846                 printRegions(out, infoStyle, currentLine.size());
0847                 out << resetBgColor;
0848             }
0849 
0850             for (const auto &fragment : m_highlightedFragments) {
0851                 auto const &ansiStyle = ansiStyles[fragment.formatId];
0852                 out << ansiStyle.first << QStringView(currentLine).mid(fragment.offset, fragment.length) << ansiStyle.second;
0853             }
0854 
0855             out << QStringLiteral("\x1b[K\n");
0856 
0857             if (hasFormatOrContextTrace && !m_highlightedFragments.empty()) {
0858                 if (m_hasContextTrace || m_hasStackSizeTrace) {
0859                     appendContextNames(oldState, currentLine);
0860                 }
0861 
0862                 printFormats(out, infoStyle, ansiStyles);
0863                 out << resetBgColor;
0864             }
0865 
0866             m_highlightedFragments.clear();
0867         }
0868     }
0869 
0870     void applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) override
0871     {
0872         m_highlightedFragments.push_back({m_hasFormatTrace ? format.name() : QString(), offset, length, format.id()});
0873     }
0874 
0875     void applyFolding(int offset, int /*length*/, FoldingRegion region) override
0876     {
0877         if (!m_hasRegionTrace) {
0878             return;
0879         }
0880 
0881         const auto id = region.id();
0882 
0883         if (region.type() == FoldingRegion::Begin) {
0884             m_regions.push_back(Region{m_regionDepth, offset, -1, id, Region::State::Open});
0885             // swap with previous region if this is a closing region with same offset in order to superimpose labels
0886             if (m_regions.size() >= 2) {
0887                 auto &previousRegion = m_regions[m_regions.size() - 2];
0888                 if (previousRegion.state == Region::State::Close && previousRegion.offset == offset) {
0889                     std::swap(previousRegion, m_regions.back());
0890                     if (previousRegion.bindIndex != -1) {
0891                         m_regions[previousRegion.bindIndex].bindIndex = m_regions.size() - 1;
0892                     }
0893                 }
0894             }
0895             ++m_regionDepth;
0896         } else {
0897             // find open region
0898             auto it = m_regions.rbegin();
0899             auto eit = m_regions.rend();
0900             for (int depth = 0; it != eit; ++it) {
0901                 if (it->regionId == id && it->bindIndex < 0) {
0902                     if (it->state == Region::State::Close) {
0903                         ++depth;
0904                     } else if (--depth < 0) {
0905                         break;
0906                     }
0907                 }
0908             }
0909 
0910             if (it != eit) {
0911                 it->bindIndex = int(m_regions.size());
0912                 int bindIndex = int(&*it - m_regions.data());
0913                 m_regions.push_back(Region{it->depth, offset, bindIndex, id, Region::State::Close});
0914             } else {
0915                 m_regions.push_back(Region{-1, offset, -1, id, Region::State::Close});
0916             }
0917 
0918             m_regionDepth = std::max(m_regionDepth - 1, 0);
0919         }
0920     }
0921 
0922     using KSyntaxHighlighting::AbstractHighlighter::highlightLine;
0923 
0924 private:
0925     /**
0926      * Initializes with colors of \p ansiStyle without duplicate.
0927      */
0928     void initRegionStyles(const std::vector<QPair<QString, QString>> &ansiStyles)
0929     {
0930         m_regionStyles.resize(ansiStyles.size());
0931         for (std::size_t i = 0; i < m_regionStyles.size(); ++i) {
0932             m_regionStyles[i] = ansiStyles[i].first;
0933         }
0934 
0935         std::sort(m_regionStyles.begin(), m_regionStyles.end());
0936         m_regionStyles.erase(std::unique(m_regionStyles.begin(), m_regionStyles.end()), m_regionStyles.end());
0937     }
0938 
0939     /**
0940      * Append the context name in front of the format name.
0941      */
0942     void appendContextNames(State &state, QStringView currentLine)
0943     {
0944         auto newState = state;
0945         for (auto &fragment : m_highlightedFragments) {
0946             QString contextName = extractContextName(StateData::get(newState));
0947 
0948             m_contextCapture.offsetNext = 0;
0949             m_contextCapture.lengthNext = 0;
0950             // truncate the line to deduce the context from the format
0951             const auto lineFragment = currentLine.mid(0, fragment.offset + fragment.length + 1);
0952             newState = m_contextCapture.highlightLine(lineFragment, state);
0953 
0954             // Deduced context does not start at the position of the format.
0955             // This can happen because of lookAhead/fallthrought attribute,
0956             // assertion in regex, etc.
0957             if (m_contextCapture.offset != fragment.offset && m_contextCapture.length != fragment.length) {
0958                 contextName.insert(0, QLatin1Char('~'));
0959             }
0960             fragment.name.insert(0, contextName);
0961         }
0962     }
0963 
0964     /**
0965      * \return Current context name with definition name if different
0966      * from the current definition name
0967      */
0968     QString extractContextName(StateData *stateData) const
0969     {
0970         QString label;
0971 
0972         if (m_hasStackSizeTrace) {
0973             label += QLatin1Char('(') % QString::number(stateData->size()) % QLatin1Char(')');
0974         }
0975 
0976         if (m_hasContextTrace) {
0977             // first state is empty
0978             if (stateData->isEmpty()) {
0979                 return label + QStringLiteral("[???]");
0980             }
0981 
0982             const auto context = stateData->topContext();
0983             const auto defDataIt = m_defDataBycontexts.find(context);
0984             const auto contextName = (defDataIt != m_defDataBycontexts.end()) ? QString(QLatin1Char('<') % (*defDataIt)->name % QLatin1Char('>')) : QString();
0985             return QString(label % contextName % QLatin1Char('[') % context->name() % QLatin1Char(']'));
0986         }
0987 
0988         return label;
0989     }
0990 
0991     void printFormats(QTextStream &out, QLatin1String regionStyle, const std::vector<QPair<QString, QString>> &ansiStyles)
0992     {
0993         // init graph
0994         m_formatGraph.clear();
0995         for (auto const &fragment : m_highlightedFragments) {
0996             GraphLine &line = lineAtOffset(m_formatGraph, fragment.offset);
0997             auto const &style = ansiStyles[fragment.formatId].first;
0998             line.pushLabel(fragment.offset, style % nameStyle % fragment.name % regionStyle, fragment.name.size());
0999 
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 
1158         using KSyntaxHighlighting::AbstractHighlighter::highlightLine;
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 
1189     QHash<const Context *, const DefinitionData *> m_defDataBycontexts;
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 }