diff --git a/Telegram/SourceFiles/ui/image/image.cpp b/Telegram/SourceFiles/ui/image/image.cpp index 17f95e354..14cefdb5a 100644 --- a/Telegram/SourceFiles/ui/image/image.cpp +++ b/Telegram/SourceFiles/ui/image/image.cpp @@ -41,260 +41,6 @@ namespace { } // namespace -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 Image::Image(const QString &path) diff --git a/Telegram/SourceFiles/ui/image/image.h b/Telegram/SourceFiles/ui/image/image.h index 1735a87a2..61095ce3c 100644 --- a/Telegram/SourceFiles/ui/image/image.h +++ b/Telegram/SourceFiles/ui/image/image.h @@ -11,14 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class QPainterPath; -namespace Images { - -[[nodiscard]] QByteArray ExpandInlineBytes(const QByteArray &bytes); -[[nodiscard]] QImage FromInlineBytes(const QByteArray &bytes); -[[nodiscard]] QPainterPath PathFromInlineBytes(const QByteArray &bytes); - -} // namespace Images - class Image final { public: explicit Image(const QString &path);