From bcfce982c851e16f10ec7a3c360404ad37975b50 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 24 Aug 2023 18:01:55 +0200 Subject: [PATCH] Move inline image/path expanding here. --- ui/image/image_prepare.cpp | 254 +++++++++++++++++++++++++++++++++++++ ui/image/image_prepare.h | 4 + 2 files changed, 258 insertions(+) diff --git a/ui/image/image_prepare.cpp b/ui/image/image_prepare.cpp index d5e9f16..5559aef 100644 --- a/ui/image/image_prepare.cpp +++ b/ui/image/image_prepare.cpp @@ -1366,4 +1366,258 @@ QByteArray MakeProgressiveJpeg(const QByteArray &bytes) { } } +QByteArray ExpandInlineBytes(const QByteArray &bytes) { + if (bytes.size() < 3 || bytes[0] != '\x01') { + return QByteArray(); + } + const char header[] = "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49" + "\x46\x00\x01\x01\x00\x00\x01\x00\x01\x00\x00\xff\xdb\x00\x43\x00\x28\x1c" + "\x1e\x23\x1e\x19\x28\x23\x21\x23\x2d\x2b\x28\x30\x3c\x64\x41\x3c\x37\x37" + "\x3c\x7b\x58\x5d\x49\x64\x91\x80\x99\x96\x8f\x80\x8c\x8a\xa0\xb4\xe6\xc3" + "\xa0\xaa\xda\xad\x8a\x8c\xc8\xff\xcb\xda\xee\xf5\xff\xff\xff\x9b\xc1\xff" + "\xff\xff\xfa\xff\xe6\xfd\xff\xf8\xff\xdb\x00\x43\x01\x2b\x2d\x2d\x3c\x35" + "\x3c\x76\x41\x41\x76\xf8\xa5\x8c\xa5\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8" + "\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8" + "\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8\xf8" + "\xf8\xf8\xf8\xf8\xf8\xff\xc0\x00\x11\x08\x00\x00\x00\x00\x03\x01\x22\x00" + "\x02\x11\x01\x03\x11\x01\xff\xc4\x00\x1f\x00\x00\x01\x05\x01\x01\x01\x01" + "\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\xff\xc4\x00\xb5\x10\x00\x02\x01\x03\x03\x02\x04\x03\x05\x05" + "\x04\x04\x00\x00\x01\x7d\x01\x02\x03\x00\x04\x11\x05\x12\x21\x31\x41\x06" + "\x13\x51\x61\x07\x22\x71\x14\x32\x81\x91\xa1\x08\x23\x42\xb1\xc1\x15\x52" + "\xd1\xf0\x24\x33\x62\x72\x82\x09\x0a\x16\x17\x18\x19\x1a\x25\x26\x27\x28" + "\x29\x2a\x34\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a\x53" + "\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74\x75" + "\x76\x77\x78\x79\x7a\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94\x95\x96" + "\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4\xb5\xb6" + "\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4\xd5\xd6" + "\xd7\xd8\xd9\xda\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf1\xf2\xf3\xf4" + "\xf5\xf6\xf7\xf8\xf9\xfa\xff\xc4\x00\x1f\x01\x00\x03\x01\x01\x01\x01\x01" + "\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08" + "\x09\x0a\x0b\xff\xc4\x00\xb5\x11\x00\x02\x01\x02\x04\x04\x03\x04\x07\x05" + "\x04\x04\x00\x01\x02\x77\x00\x01\x02\x03\x11\x04\x05\x21\x31\x06\x12\x41" + "\x51\x07\x61\x71\x13\x22\x32\x81\x08\x14\x42\x91\xa1\xb1\xc1\x09\x23\x33" + "\x52\xf0\x15\x62\x72\xd1\x0a\x16\x24\x34\xe1\x25\xf1\x17\x18\x19\x1a\x26" + "\x27\x28\x29\x2a\x35\x36\x37\x38\x39\x3a\x43\x44\x45\x46\x47\x48\x49\x4a" + "\x53\x54\x55\x56\x57\x58\x59\x5a\x63\x64\x65\x66\x67\x68\x69\x6a\x73\x74" + "\x75\x76\x77\x78\x79\x7a\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x92\x93\x94" + "\x95\x96\x97\x98\x99\x9a\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xb2\xb3\xb4" + "\xb5\xb6\xb7\xb8\xb9\xba\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xd2\xd3\xd4" + "\xd5\xd6\xd7\xd8\xd9\xda\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xf2\xf3\xf4" + "\xf5\xf6\xf7\xf8\xf9\xfa\xff\xda\x00\x0c\x03\x01\x00\x02\x11\x03\x11\x00" + "\x3f\x00"; + const char footer[] = "\xff\xd9"; + auto real = QByteArray(header, sizeof(header) - 1); + real[164] = bytes[1]; + real[166] = bytes[2]; + return real + + bytes.mid(3) + + QByteArray::fromRawData(footer, sizeof(footer) - 1); +} + +QImage FromInlineBytes(const QByteArray &bytes) { + return Read({ .content = ExpandInlineBytes(bytes) }).image; +} + +// Thanks TDLib for code. +QByteArray ExpandPathInlineBytes(const QByteArray &bytes) { + auto result = QByteArray(); + result.reserve(3 * (bytes.size() + 1)); + result.append('M'); + for (unsigned char c : bytes) { + if (c >= 128 + 64) { + result.append("AACAAAAHAAALMAAAQASTAVAAAZ" + "aacaaaahaaalmaaaqastava.az0123456789-,"[c - 128 - 64]); + } else { + if (c >= 128) { + result.append(','); + } else if (c >= 64) { + result.append('-'); + } + //char buffer[3] = { 0 }; // Unavailable on macOS < 10.15. + //std::to_chars(buffer, buffer + 3, (c & 63)); + //result.append(buffer); + result.append(QByteArray::number(c & 63)); + } + } + result.append('z'); + return result; +} + +QPainterPath PathFromInlineBytes(const QByteArray &bytes) { + if (bytes.isEmpty()) { + return QPainterPath(); + } + const auto expanded = ExpandPathInlineBytes(bytes); + const auto path = expanded.data(); // Allows checking for '\0' by index. + auto position = 0; + + const auto isAlpha = [](char c) { + c |= 0x20; + return 'a' <= c && c <= 'z'; + }; + const auto isDigit = [](char c) { + return '0' <= c && c <= '9'; + }; + const auto skipCommas = [&] { + while (path[position] == ',') { + ++position; + } + }; + const auto getNumber = [&] { + skipCommas(); + auto sign = 1; + if (path[position] == '-') { + sign = -1; + ++position; + } + double res = 0; + while (isDigit(path[position])) { + res = res * 10 + path[position++] - '0'; + } + if (path[position] == '.') { + ++position; + double mul = 0.1; + while (isDigit(path[position])) { + res += (path[position] - '0') * mul; + mul *= 0.1; + ++position; + } + } + return sign * res; + }; + + auto result = QPainterPath(); + auto x = 0.; + auto y = 0.; + while (path[position] != '\0') { + skipCommas(); + if (path[position] == '\0') { + break; + } + + while (path[position] == 'm' || path[position] == 'M') { + auto command = path[position++]; + do { + if (command == 'm') { + x += getNumber(); + y += getNumber(); + } else { + x = getNumber(); + y = getNumber(); + } + skipCommas(); + } while (path[position] != '\0' && !isAlpha(path[position])); + } + + auto xStart = x; + auto yStart = y; + result.moveTo(xStart, yStart); + auto haveLastEndControlPoint = false; + auto xLastEndControlPoint = 0.; + auto yLastEndControlPoint = 0.; + auto isClosed = false; + auto command = '-'; + while (!isClosed) { + skipCommas(); + if (path[position] == '\0') { + LOG(("SVG Error: Receive unclosed path: %1" + ).arg(QString::fromLatin1(path))); + return QPainterPath(); + } + if (isAlpha(path[position])) { + command = path[position++]; + } + switch (command) { + case 'l': + case 'L': + case 'h': + case 'H': + case 'v': + case 'V': + if (command == 'l' || command == 'h') { + x += getNumber(); + } else if (command == 'L' || command == 'H') { + x = getNumber(); + } + if (command == 'l' || command == 'v') { + y += getNumber(); + } else if (command == 'L' || command == 'V') { + y = getNumber(); + } + result.lineTo(x, y); + haveLastEndControlPoint = false; + break; + case 'C': + case 'c': + case 'S': + case 's': { + auto xStartControlPoint = 0.; + auto yStartControlPoint = 0.; + if (command == 'S' || command == 's') { + if (haveLastEndControlPoint) { + xStartControlPoint = 2 * x - xLastEndControlPoint; + yStartControlPoint = 2 * y - yLastEndControlPoint; + } else { + xStartControlPoint = x; + yStartControlPoint = y; + } + } else { + xStartControlPoint = getNumber(); + yStartControlPoint = getNumber(); + if (command == 'c') { + xStartControlPoint += x; + yStartControlPoint += y; + } + } + + xLastEndControlPoint = getNumber(); + yLastEndControlPoint = getNumber(); + if (command == 'c' || command == 's') { + xLastEndControlPoint += x; + yLastEndControlPoint += y; + } + haveLastEndControlPoint = true; + + if (command == 'c' || command == 's') { + x += getNumber(); + y += getNumber(); + } else { + x = getNumber(); + y = getNumber(); + } + result.cubicTo( + xStartControlPoint, + yStartControlPoint, + xLastEndControlPoint, + yLastEndControlPoint, + x, + y); + break; + } + case 'm': + case 'M': + --position; + [[fallthrough]]; + case 'z': + case 'Z': + if (x != xStart || y != yStart) { + x = xStart; + y = yStart; + result.lineTo(x, y); + } + isClosed = true; + break; + default: + LOG(("SVG Error: Receive invalid command %1 at pos %2: %3" + ).arg(command + ).arg(position + ).arg(QString::fromLatin1(path))); + return QPainterPath(); + } + } + } + return result; +} + } // namespace Images diff --git a/ui/image/image_prepare.h b/ui/image/image_prepare.h index c312a02..6368c0e 100644 --- a/ui/image/image_prepare.h +++ b/ui/image/image_prepare.h @@ -206,4 +206,8 @@ struct PrepareArgs { [[nodiscard]] bool IsProgressiveJpeg(const QByteArray &bytes); [[nodiscard]] QByteArray MakeProgressiveJpeg(const QByteArray &bytes); +[[nodiscard]] QByteArray ExpandInlineBytes(const QByteArray &bytes); +[[nodiscard]] QImage FromInlineBytes(const QByteArray &bytes); +[[nodiscard]] QPainterPath PathFromInlineBytes(const QByteArray &bytes); + } // namespace Images