File indexing completed on 2024-05-12 04:02:17

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