diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fdd1a519..871bd8848 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -400,6 +400,7 @@ set(TDLIB_SOURCE td/telegram/PhoneNumberManager.cpp td/telegram/PrivacyManager.cpp td/telegram/Photo.cpp + td/telegram/PhotoSize.cpp td/telegram/PhotoSizeSource.cpp td/telegram/PollManager.cpp td/telegram/QueryCombiner.cpp @@ -625,6 +626,7 @@ set(TDLIB_SOURCE td/telegram/Payments.h td/telegram/PhoneNumberManager.h td/telegram/Photo.h + td/telegram/PhotoSize.h td/telegram/PhotoSizeSource.h td/telegram/PollId.h td/telegram/PollManager.h @@ -702,6 +704,7 @@ set(TDLIB_SOURCE td/telegram/NotificationSettings.hpp td/telegram/Payments.hpp td/telegram/Photo.hpp + td/telegram/PhotoSize.hpp td/telegram/PhotoSizeSource.hpp td/telegram/PollId.hpp td/telegram/PollManager.hpp diff --git a/td/telegram/AnimationsManager.h b/td/telegram/AnimationsManager.h index 2c7b40c01..e9d9a65ad 100644 --- a/td/telegram/AnimationsManager.h +++ b/td/telegram/AnimationsManager.h @@ -8,7 +8,7 @@ #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileSourceId.h" -#include "td/telegram/Photo.h" +#include "td/telegram/PhotoSize.h" #include "td/telegram/SecretInputMedia.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" diff --git a/td/telegram/AnimationsManager.hpp b/td/telegram/AnimationsManager.hpp index c5af73311..af7749967 100644 --- a/td/telegram/AnimationsManager.hpp +++ b/td/telegram/AnimationsManager.hpp @@ -9,7 +9,7 @@ #include "td/telegram/AnimationsManager.h" #include "td/telegram/files/FileId.hpp" -#include "td/telegram/Photo.hpp" +#include "td/telegram/PhotoSize.hpp" #include "td/telegram/Version.h" #include "td/utils/common.h" diff --git a/td/telegram/AudiosManager.h b/td/telegram/AudiosManager.h index 8d158acb9..bcb63f787 100644 --- a/td/telegram/AudiosManager.h +++ b/td/telegram/AudiosManager.h @@ -7,7 +7,7 @@ #pragma once #include "td/telegram/files/FileId.h" -#include "td/telegram/Photo.h" +#include "td/telegram/PhotoSize.h" #include "td/telegram/SecretInputMedia.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" diff --git a/td/telegram/AudiosManager.hpp b/td/telegram/AudiosManager.hpp index 8a999e04c..eebb33998 100644 --- a/td/telegram/AudiosManager.hpp +++ b/td/telegram/AudiosManager.hpp @@ -9,7 +9,7 @@ #include "td/telegram/AudiosManager.h" #include "td/telegram/files/FileId.hpp" -#include "td/telegram/Photo.hpp" +#include "td/telegram/PhotoSize.hpp" #include "td/telegram/Version.h" #include "td/utils/common.h" diff --git a/td/telegram/DialogEventLog.cpp b/td/telegram/DialogEventLog.cpp index 0c2ca3794..67bfa00d8 100644 --- a/td/telegram/DialogEventLog.cpp +++ b/td/telegram/DialogEventLog.cpp @@ -18,6 +18,7 @@ #include "td/telegram/MessageSender.h" #include "td/telegram/MessagesManager.h" #include "td/telegram/MessageTtl.h" +#include "td/telegram/Photo.h" #include "td/telegram/StickersManager.h" #include "td/telegram/Td.h" #include "td/telegram/telegram_api.h" diff --git a/td/telegram/DocumentsManager.h b/td/telegram/DocumentsManager.h index c6e139871..e60815f71 100644 --- a/td/telegram/DocumentsManager.h +++ b/td/telegram/DocumentsManager.h @@ -10,8 +10,8 @@ #include "td/telegram/Document.h" #include "td/telegram/EncryptedFile.h" #include "td/telegram/files/FileId.h" -#include "td/telegram/Photo.h" #include "td/telegram/PhotoFormat.h" +#include "td/telegram/PhotoSize.h" #include "td/telegram/secret_api.h" #include "td/telegram/SecretInputMedia.h" #include "td/telegram/td_api.h" diff --git a/td/telegram/DocumentsManager.hpp b/td/telegram/DocumentsManager.hpp index 3533c3949..ed20b2bee 100644 --- a/td/telegram/DocumentsManager.hpp +++ b/td/telegram/DocumentsManager.hpp @@ -9,7 +9,7 @@ #include "td/telegram/DocumentsManager.h" #include "td/telegram/files/FileId.hpp" -#include "td/telegram/Photo.hpp" +#include "td/telegram/PhotoSize.hpp" #include "td/telegram/Version.h" #include "td/utils/logging.h" diff --git a/td/telegram/InlineQueriesManager.cpp b/td/telegram/InlineQueriesManager.cpp index b98926a60..cfb57bef5 100644 --- a/td/telegram/InlineQueriesManager.cpp +++ b/td/telegram/InlineQueriesManager.cpp @@ -29,6 +29,7 @@ #include "td/telegram/Payments.h" #include "td/telegram/Photo.h" #include "td/telegram/PhotoFormat.h" +#include "td/telegram/PhotoSize.h" #include "td/telegram/ReplyMarkup.h" #include "td/telegram/StickersManager.h" #include "td/telegram/Td.h" diff --git a/td/telegram/MessageContent.cpp b/td/telegram/MessageContent.cpp index f7c1f28ae..e60a3a5da 100644 --- a/td/telegram/MessageContent.cpp +++ b/td/telegram/MessageContent.cpp @@ -47,6 +47,7 @@ #include "td/telegram/Photo.h" #include "td/telegram/Photo.hpp" #include "td/telegram/PhotoFormat.h" +#include "td/telegram/PhotoSize.h" #include "td/telegram/PhotoSizeSource.h" #include "td/telegram/PollId.h" #include "td/telegram/PollId.hpp" diff --git a/td/telegram/MessagesManager.h b/td/telegram/MessagesManager.h index 49fa3bb74..5de888242 100644 --- a/td/telegram/MessagesManager.h +++ b/td/telegram/MessagesManager.h @@ -44,6 +44,7 @@ #include "td/telegram/NotificationGroupType.h" #include "td/telegram/NotificationId.h" #include "td/telegram/NotificationSettings.h" +#include "td/telegram/Photo.h" #include "td/telegram/RecentDialogList.h" #include "td/telegram/ReplyMarkup.h" #include "td/telegram/ReportReason.h" diff --git a/td/telegram/NotificationManager.h b/td/telegram/NotificationManager.h index 8e54d1bcf..08d14f17c 100644 --- a/td/telegram/NotificationManager.h +++ b/td/telegram/NotificationManager.h @@ -17,6 +17,7 @@ #include "td/telegram/NotificationGroupType.h" #include "td/telegram/NotificationId.h" #include "td/telegram/NotificationType.h" +#include "td/telegram/Photo.h" #include "td/telegram/td_api.h" #include "td/actor/actor.h" diff --git a/td/telegram/Payments.cpp b/td/telegram/Payments.cpp index 6f9031ec9..96ef21013 100644 --- a/td/telegram/Payments.cpp +++ b/td/telegram/Payments.cpp @@ -14,6 +14,7 @@ #include "td/telegram/MessagesManager.h" #include "td/telegram/misc.h" #include "td/telegram/PasswordManager.h" +#include "td/telegram/PhotoSize.h" #include "td/telegram/ServerMessageId.h" #include "td/telegram/Td.h" #include "td/telegram/ThemeManager.h" diff --git a/td/telegram/Photo.cpp b/td/telegram/Photo.cpp index f9f3d20d1..6fe1f3219 100644 --- a/td/telegram/Photo.cpp +++ b/td/telegram/Photo.cpp @@ -9,149 +9,22 @@ #include "td/telegram/files/FileEncryptionKey.h" #include "td/telegram/files/FileLocation.h" #include "td/telegram/files/FileManager.h" +#include "td/telegram/files/FileType.h" #include "td/telegram/net/DcId.h" +#include "td/telegram/PhotoFormat.h" +#include "td/telegram/PhotoSizeSource.h" #include "td/utils/algorithm.h" -#include "td/utils/base64.h" #include "td/utils/common.h" #include "td/utils/format.h" -#include "td/utils/HttpUrl.h" #include "td/utils/logging.h" #include "td/utils/misc.h" -#include "td/utils/Random.h" #include "td/utils/SliceBuilder.h" -#include #include -#include namespace td { -static uint16 get_dimension(int32 size, const char *source) { - if (size < 0 || size > 65535) { - LOG(ERROR) << "Wrong image dimension = " << size << " from " << source; - return 0; - } - return narrow_cast(size); -} - -Dimensions get_dimensions(int32 width, int32 height, const char *source) { - Dimensions result; - result.width = get_dimension(width, source); - result.height = get_dimension(height, source); - if (result.width == 0 || result.height == 0) { - result.width = 0; - result.height = 0; - } - return result; -} - -static uint32 get_pixel_count(const Dimensions &dimensions) { - return static_cast(dimensions.width) * static_cast(dimensions.height); -} - -bool operator==(const Dimensions &lhs, const Dimensions &rhs) { - return lhs.width == rhs.width && lhs.height == rhs.height; -} - -bool operator!=(const Dimensions &lhs, const Dimensions &rhs) { - return !(lhs == rhs); -} - -StringBuilder &operator<<(StringBuilder &string_builder, const Dimensions &dimensions) { - return string_builder << "(" << dimensions.width << ", " << dimensions.height << ")"; -} - -td_api::object_ptr get_minithumbnail_object(const string &packed) { - if (packed.size() < 3) { - return nullptr; - } - if (packed[0] == '\x01') { - static const string header = - base64_decode( - "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACgcHiMeGSgjISMtKygwPGRBPDc3PHtYXUlkkYCZlo+AjIqgtObDoKrarYqMyP/L2u71////" - "m8H///" - "/6/+b9//j/2wBDASstLTw1PHZBQXb4pYyl+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj/" - "wAARCAAAAAADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/" - "8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0R" - "FRkd" - "ISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2" - "uHi4" - "+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/" - "8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkN" - "ERUZ" - "HSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2" - "Nna4" - "uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwA=") - .move_as_ok(); - static const string footer = base64_decode("/9k=").move_as_ok(); - auto result = td_api::make_object(); - result->height_ = static_cast(packed[1]); - result->width_ = static_cast(packed[2]); - result->data_ = PSTRING() << header.substr(0, 164) << packed[1] << header[165] << packed[2] << header.substr(167) - << packed.substr(3) << footer; - return result; - } - return nullptr; -} - -static td_api::object_ptr get_thumbnail_format_object(PhotoFormat format) { - switch (format) { - case PhotoFormat::Jpeg: - return td_api::make_object(); - case PhotoFormat::Png: - return td_api::make_object(); - case PhotoFormat::Webp: - return td_api::make_object(); - case PhotoFormat::Gif: - return td_api::make_object(); - case PhotoFormat::Tgs: - return td_api::make_object(); - case PhotoFormat::Mpeg4: - return td_api::make_object(); - case PhotoFormat::Webm: - return td_api::make_object(); - default: - UNREACHABLE(); - return nullptr; - } -} - -static StringBuilder &operator<<(StringBuilder &string_builder, PhotoFormat format) { - switch (format) { - case PhotoFormat::Jpeg: - return string_builder << "jpg"; - case PhotoFormat::Png: - return string_builder << "png"; - case PhotoFormat::Webp: - return string_builder << "webp"; - case PhotoFormat::Gif: - return string_builder << "gif"; - case PhotoFormat::Tgs: - return string_builder << "tgs"; - case PhotoFormat::Mpeg4: - return string_builder << "mp4"; - case PhotoFormat::Webm: - return string_builder << "webm"; - default: - UNREACHABLE(); - return string_builder; - } -} - -static FileId register_photo(FileManager *file_manager, const PhotoSizeSource &source, int64 id, int64 access_hash, - std::string file_reference, DialogId owner_dialog_id, int32 file_size, DcId dc_id, - PhotoFormat format) { - LOG(DEBUG) << "Receive " << format << " photo " << id << " of type " << source.get_file_type("register_photo") - << " from " << dc_id; - auto suggested_name = PSTRING() << source.get_unique_name(id) << '.' << format; - auto file_location_source = owner_dialog_id.get_type() == DialogType::SecretChat ? FileLocationSource::FromUser - : FileLocationSource::FromServer; - return file_manager->register_remote( - FullRemoteFileLocation(source, id, access_hash, dc_id, std::move(file_reference)), file_location_source, - owner_dialog_id, file_size, 0, std::move(suggested_name)); -} - ProfilePhoto get_profile_photo(FileManager *file_manager, UserId user_id, int64 user_access_hash, tl_object_ptr &&profile_photo_ptr) { ProfilePhoto result; @@ -167,10 +40,10 @@ ProfilePhoto get_profile_photo(FileManager *file_manager, UserId user_id, int64 result.has_animation = profile_photo->has_video_; result.id = profile_photo->photo_id_; result.minithumbnail = profile_photo->stripped_thumb_.as_slice().str(); - result.small_file_id = register_photo( + result.small_file_id = register_photo_size( file_manager, PhotoSizeSource::dialog_photo(DialogId(user_id), user_access_hash, false), result.id, 0 /*access_hash*/, "" /*file_reference*/, DialogId(), 0 /*file_size*/, dc_id, PhotoFormat::Jpeg); - result.big_file_id = register_photo( + result.big_file_id = register_photo_size( file_manager, PhotoSizeSource::dialog_photo(DialogId(user_id), user_access_hash, true), result.id, 0 /*access_hash*/, "" /*file_reference*/, DialogId(), 0 /*file_size*/, dc_id, PhotoFormat::Jpeg); break; @@ -229,11 +102,11 @@ DialogPhoto get_dialog_photo(FileManager *file_manager, DialogId dialog_id, int6 result.has_animation = chat_photo->has_video_; result.minithumbnail = chat_photo->stripped_thumb_.as_slice().str(); result.small_file_id = - register_photo(file_manager, PhotoSizeSource::dialog_photo(dialog_id, dialog_access_hash, false), - chat_photo->photo_id_, 0, "", DialogId(), 0, dc_id, PhotoFormat::Jpeg); + register_photo_size(file_manager, PhotoSizeSource::dialog_photo(dialog_id, dialog_access_hash, false), + chat_photo->photo_id_, 0, "", DialogId(), 0, dc_id, PhotoFormat::Jpeg); result.big_file_id = - register_photo(file_manager, PhotoSizeSource::dialog_photo(dialog_id, dialog_access_hash, true), - chat_photo->photo_id_, 0, "", DialogId(), 0, dc_id, PhotoFormat::Jpeg); + register_photo_size(file_manager, PhotoSizeSource::dialog_photo(dialog_id, dialog_access_hash, true), + chat_photo->photo_id_, 0, "", DialogId(), 0, dc_id, PhotoFormat::Jpeg); break; } @@ -327,254 +200,6 @@ StringBuilder &operator<<(StringBuilder &string_builder, const DialogPhoto &dial << ", has_animation = " << dialog_photo.has_animation << ">"; } -PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice bytes, DialogId owner_dialog_id, - int32 width, int32 height) { - if (bytes.empty()) { - return PhotoSize(); - } - PhotoSize res; - res.type = 't'; - res.dimensions = get_dimensions(width, height, "get_secret_thumbnail_photo_size"); - res.size = narrow_cast(bytes.size()); - - // generate some random remote location to save - auto dc_id = DcId::invalid(); - auto photo_id = -(Random::secure_int64() & std::numeric_limits::max()); - - res.file_id = file_manager->register_remote( - FullRemoteFileLocation(PhotoSizeSource::thumbnail(FileType::EncryptedThumbnail, 't'), photo_id, 0, dc_id, - string()), - FileLocationSource::FromServer, owner_dialog_id, res.size, 0, - PSTRING() << static_cast(photo_id) << ".jpg"); - file_manager->set_content(res.file_id, std::move(bytes)); - - return res; -} - -Variant get_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id, - int64 access_hash, std::string file_reference, DcId dc_id, - DialogId owner_dialog_id, tl_object_ptr &&size_ptr, - PhotoFormat format) { - CHECK(size_ptr != nullptr); - - string type; - PhotoSize res; - BufferSlice content; - switch (size_ptr->get_id()) { - case telegram_api::photoSizeEmpty::ID: - return std::move(res); - case telegram_api::photoSize::ID: { - auto size = move_tl_object_as(size_ptr); - - type = std::move(size->type_); - res.dimensions = get_dimensions(size->w_, size->h_, "photoSize"); - res.size = size->size_; - - break; - } - case telegram_api::photoCachedSize::ID: { - auto size = move_tl_object_as(size_ptr); - - type = std::move(size->type_); - CHECK(size->bytes_.size() <= static_cast(std::numeric_limits::max())); - res.dimensions = get_dimensions(size->w_, size->h_, "photoCachedSize"); - res.size = static_cast(size->bytes_.size()); - - content = std::move(size->bytes_); - - break; - } - case telegram_api::photoStrippedSize::ID: { - auto size = move_tl_object_as(size_ptr); - if (format != PhotoFormat::Jpeg) { - LOG(ERROR) << "Receive unexpected JPEG minithumbnail in photo " << id << " from " << source << " of format " - << format; - return std::move(res); - } - return size->bytes_.as_slice().str(); - } - case telegram_api::photoSizeProgressive::ID: { - auto size = move_tl_object_as(size_ptr); - - if (size->sizes_.empty()) { - LOG(ERROR) << "Receive photo " << id << " from " << source << " with empty size " << to_string(size); - return std::move(res); - } - std::sort(size->sizes_.begin(), size->sizes_.end()); - - type = std::move(size->type_); - res.dimensions = get_dimensions(size->w_, size->h_, "photoSizeProgressive"); - res.size = size->sizes_.back(); - size->sizes_.pop_back(); - res.progressive_sizes = std::move(size->sizes_); - - break; - } - case telegram_api::photoPathSize::ID: { - auto size = move_tl_object_as(size_ptr); - if (format != PhotoFormat::Tgs && format != PhotoFormat::Webp && format != PhotoFormat::Webm) { - LOG(ERROR) << "Receive unexpected SVG minithumbnail in photo " << id << " from " << source << " of format " - << format; - return std::move(res); - } - return size->bytes_.as_slice().str(); - } - default: - UNREACHABLE(); - break; - } - - if (type.size() != 1) { - LOG(ERROR) << "Wrong photoSize \"" << type << "\" " << res; - res.type = 0; - } else { - res.type = static_cast(type[0]); - if (res.type >= 128) { - LOG(ERROR) << "Wrong photoSize \"" << type << "\" " << res; - res.type = 0; - } - } - if (source.get_type("get_photo_size") == PhotoSizeSource::Type::Thumbnail) { - source.thumbnail().thumbnail_type = res.type; - } - - res.file_id = register_photo(file_manager, source, id, access_hash, std::move(file_reference), owner_dialog_id, - res.size, dc_id, format); - - if (!content.empty()) { - file_manager->set_content(res.file_id, std::move(content)); - } - - return std::move(res); -} - -AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash, - std::string file_reference, DcId dc_id, DialogId owner_dialog_id, - tl_object_ptr &&size) { - CHECK(size != nullptr); - AnimationSize res; - if (size->type_ != "v" && size->type_ != "u") { - LOG(ERROR) << "Wrong videoSize \"" << size->type_ << "\" in " << to_string(size); - } - res.type = static_cast(size->type_[0]); - if (res.type >= 128) { - LOG(ERROR) << "Wrong videoSize \"" << res.type << "\" " << res; - res.type = 0; - } - res.dimensions = get_dimensions(size->w_, size->h_, "get_animation_size"); - res.size = size->size_; - if ((size->flags_ & telegram_api::videoSize::VIDEO_START_TS_MASK) != 0) { - res.main_frame_timestamp = size->video_start_ts_; - } - - if (source.get_type("get_animation_size") == PhotoSizeSource::Type::Thumbnail) { - source.thumbnail().thumbnail_type = res.type; - } - - res.file_id = register_photo(file_manager, source, id, access_hash, std::move(file_reference), owner_dialog_id, - res.size, dc_id, PhotoFormat::Mpeg4); - return res; -} - -PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id, - tl_object_ptr web_document_ptr) { - if (web_document_ptr == nullptr) { - return {}; - } - - FileId file_id; - vector> attributes; - int32 size = 0; - string mime_type; - switch (web_document_ptr->get_id()) { - case telegram_api::webDocument::ID: { - auto web_document = move_tl_object_as(web_document_ptr); - auto r_http_url = parse_url(web_document->url_); - if (r_http_url.is_error()) { - LOG(ERROR) << "Can't parse URL " << web_document->url_; - return {}; - } - auto http_url = r_http_url.move_as_ok(); - auto url = http_url.get_url(); - file_id = file_manager->register_remote(FullRemoteFileLocation(file_type, url, web_document->access_hash_), - FileLocationSource::FromServer, owner_dialog_id, 0, web_document->size_, - get_url_query_file_name(http_url.query_)); - size = web_document->size_; - mime_type = std::move(web_document->mime_type_); - attributes = std::move(web_document->attributes_); - break; - } - case telegram_api::webDocumentNoProxy::ID: { - auto web_document = move_tl_object_as(web_document_ptr); - if (web_document->url_.find('.') == string::npos) { - LOG(ERROR) << "Receive invalid URL " << web_document->url_; - return {}; - } - - auto r_file_id = file_manager->from_persistent_id(web_document->url_, file_type); - if (r_file_id.is_error()) { - LOG(ERROR) << "Can't register URL: " << r_file_id.error(); - return {}; - } - file_id = r_file_id.move_as_ok(); - - size = web_document->size_; - mime_type = std::move(web_document->mime_type_); - attributes = std::move(web_document->attributes_); - break; - } - default: - UNREACHABLE(); - } - CHECK(file_id.is_valid()); - bool is_animation = mime_type == "video/mp4"; - bool is_gif = mime_type == "image/gif"; - - Dimensions dimensions; - for (auto &attribute : attributes) { - switch (attribute->get_id()) { - case telegram_api::documentAttributeImageSize::ID: { - auto image_size = move_tl_object_as(attribute); - dimensions = get_dimensions(image_size->w_, image_size->h_, "web documentAttributeImageSize"); - break; - } - case telegram_api::documentAttributeAnimated::ID: - case telegram_api::documentAttributeHasStickers::ID: - case telegram_api::documentAttributeSticker::ID: - case telegram_api::documentAttributeVideo::ID: - case telegram_api::documentAttributeAudio::ID: - LOG(ERROR) << "Unexpected web document attribute " << to_string(attribute); - break; - case telegram_api::documentAttributeFilename::ID: - break; - default: - UNREACHABLE(); - } - } - - PhotoSize s; - s.type = is_animation ? 'v' : (is_gif ? 'g' : (file_type == FileType::Thumbnail ? 't' : 'n')); - s.dimensions = dimensions; - s.size = size; - s.file_id = file_id; - return s; -} - -td_api::object_ptr get_thumbnail_object(FileManager *file_manager, const PhotoSize &photo_size, - PhotoFormat format) { - if (!photo_size.file_id.is_valid()) { - return nullptr; - } - - if (format == PhotoFormat::Jpeg && photo_size.type == 'g') { - format = PhotoFormat::Gif; - } - - return td_api::make_object(get_thumbnail_format_object(format), photo_size.dimensions.width, - photo_size.dimensions.height, - file_manager->get_file_object(photo_size.file_id)); -} - static tl_object_ptr get_photo_size_object(FileManager *file_manager, const PhotoSize *photo_size) { if (photo_size == nullptr || !photo_size->file_id.is_valid()) { return nullptr; @@ -605,41 +230,6 @@ static vector> get_photo_sizes_object(File return sizes; } -bool operator==(const PhotoSize &lhs, const PhotoSize &rhs) { - return lhs.type == rhs.type && lhs.dimensions == rhs.dimensions && lhs.size == rhs.size && - lhs.file_id == rhs.file_id && lhs.progressive_sizes == rhs.progressive_sizes; -} - -bool operator!=(const PhotoSize &lhs, const PhotoSize &rhs) { - return !(lhs == rhs); -} - -bool operator<(const PhotoSize &lhs, const PhotoSize &rhs) { - if (lhs.size != rhs.size) { - return lhs.size < rhs.size; - } - auto lhs_pixels = get_pixel_count(lhs.dimensions); - auto rhs_pixels = get_pixel_count(rhs.dimensions); - if (lhs_pixels != rhs_pixels) { - return lhs_pixels < rhs_pixels; - } - int32 lhs_type = lhs.type == 't' ? -1 : lhs.type; - int32 rhs_type = rhs.type == 't' ? -1 : rhs.type; - if (lhs_type != rhs_type) { - return lhs_type < rhs_type; - } - if (lhs.file_id != rhs.file_id) { - return lhs.file_id.get() < rhs.file_id.get(); - } - return lhs.dimensions.width < rhs.dimensions.width; -} - -StringBuilder &operator<<(StringBuilder &string_builder, const PhotoSize &photo_size) { - return string_builder << "{type = " << photo_size.type << ", dimensions = " << photo_size.dimensions - << ", size = " << photo_size.size << ", file_id = " << photo_size.file_id - << ", progressive_sizes = " << photo_size.progressive_sizes << "}"; -} - static tl_object_ptr get_animated_chat_photo_object(FileManager *file_manager, const AnimationSize *animation_size) { if (animation_size == nullptr || !animation_size->file_id.is_valid()) { @@ -651,20 +241,6 @@ static tl_object_ptr get_animated_chat_photo_object(F animation_size->main_frame_timestamp); } -bool operator==(const AnimationSize &lhs, const AnimationSize &rhs) { - return static_cast(lhs) == static_cast(rhs) && - fabs(lhs.main_frame_timestamp - rhs.main_frame_timestamp) < 1e-3; -} - -bool operator!=(const AnimationSize &lhs, const AnimationSize &rhs) { - return !(lhs == rhs); -} - -StringBuilder &operator<<(StringBuilder &string_builder, const AnimationSize &animation_size) { - return string_builder << static_cast(animation_size) << " from " - << animation_size.main_frame_timestamp; -} - Photo get_encrypted_file_photo(FileManager *file_manager, unique_ptr &&file, tl_object_ptr &&photo, DialogId owner_dialog_id) { diff --git a/td/telegram/Photo.h b/td/telegram/Photo.h index e3f5bbf78..0b2953082 100644 --- a/td/telegram/Photo.h +++ b/td/telegram/Photo.h @@ -9,10 +9,7 @@ #include "td/telegram/DialogId.h" #include "td/telegram/EncryptedFile.h" #include "td/telegram/files/FileId.h" -#include "td/telegram/files/FileType.h" -#include "td/telegram/net/DcId.h" -#include "td/telegram/PhotoFormat.h" -#include "td/telegram/PhotoSizeSource.h" +#include "td/telegram/PhotoSize.h" #include "td/telegram/secret_api.h" #include "td/telegram/SecretInputMedia.h" #include "td/telegram/td_api.h" @@ -23,17 +20,11 @@ #include "td/utils/common.h" #include "td/utils/MovableValue.h" #include "td/utils/StringBuilder.h" -#include "td/utils/Variant.h" namespace td { class FileManager; -struct Dimensions { - uint16 width = 0; - uint16 height = 0; -}; - struct DialogPhoto { FileId small_file_id; FileId big_file_id; @@ -45,18 +36,6 @@ struct ProfilePhoto final : public DialogPhoto { int64 id = 0; }; -struct PhotoSize { - int32 type = 0; - Dimensions dimensions; - int32 size = 0; - FileId file_id; - vector progressive_sizes; -}; - -struct AnimationSize final : public PhotoSize { - double main_frame_timestamp = 0.0; -}; - struct Photo { MovableValue id; int32 date = 0; @@ -73,15 +52,6 @@ struct Photo { } }; -Dimensions get_dimensions(int32 width, int32 height, const char *source); - -bool operator==(const Dimensions &lhs, const Dimensions &rhs); -bool operator!=(const Dimensions &lhs, const Dimensions &rhs); - -StringBuilder &operator<<(StringBuilder &string_builder, const Dimensions &dimensions); - -td_api::object_ptr get_minithumbnail_object(const string &packed); - ProfilePhoto get_profile_photo(FileManager *file_manager, UserId user_id, int64 user_access_hash, tl_object_ptr &&profile_photo_ptr); tl_object_ptr get_profile_photo_object(FileManager *file_manager, @@ -108,32 +78,6 @@ bool operator!=(const DialogPhoto &lhs, const DialogPhoto &rhs); StringBuilder &operator<<(StringBuilder &string_builder, const DialogPhoto &dialog_photo); -PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice bytes, DialogId owner_dialog_id, - int32 width, int32 height); -Variant get_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id, - int64 access_hash, string file_reference, DcId dc_id, - DialogId owner_dialog_id, tl_object_ptr &&size_ptr, - PhotoFormat format); -AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash, - string file_reference, DcId dc_id, DialogId owner_dialog_id, - tl_object_ptr &&size); -PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id, - tl_object_ptr web_document_ptr); -td_api::object_ptr get_thumbnail_object(FileManager *file_manager, const PhotoSize &photo_size, - PhotoFormat format); - -bool operator==(const PhotoSize &lhs, const PhotoSize &rhs); -bool operator!=(const PhotoSize &lhs, const PhotoSize &rhs); - -bool operator<(const PhotoSize &lhs, const PhotoSize &rhs); - -StringBuilder &operator<<(StringBuilder &string_builder, const PhotoSize &photo_size); - -bool operator==(const AnimationSize &lhs, const AnimationSize &rhs); -bool operator!=(const AnimationSize &lhs, const AnimationSize &rhs); - -StringBuilder &operator<<(StringBuilder &string_builder, const AnimationSize &animation_size); - Photo get_photo(FileManager *file_manager, tl_object_ptr &&photo, DialogId owner_dialog_id); Photo get_photo(FileManager *file_manager, tl_object_ptr &&photo, DialogId owner_dialog_id); Photo get_encrypted_file_photo(FileManager *file_manager, unique_ptr &&file, diff --git a/td/telegram/Photo.hpp b/td/telegram/Photo.hpp index 30d333229..1003e9529 100644 --- a/td/telegram/Photo.hpp +++ b/td/telegram/Photo.hpp @@ -8,26 +8,13 @@ #include "td/telegram/files/FileId.hpp" #include "td/telegram/Photo.h" +#include "td/telegram/PhotoSize.hpp" #include "td/telegram/Version.h" -#include "td/utils/logging.h" #include "td/utils/tl_helpers.h" namespace td { -template -void store(Dimensions dimensions, StorerT &storer) { - store(static_cast((static_cast(dimensions.width) << 16) | dimensions.height), storer); -} - -template -void parse(Dimensions &dimensions, ParserT &parser) { - uint32 width_height; - parse(width_height, parser); - dimensions.width = static_cast(width_height >> 16); - dimensions.height = static_cast(width_height & 0xFFFF); -} - template void store(const DialogPhoto &dialog_photo, StorerT &storer) { bool has_file_ids = dialog_photo.small_file_id.is_valid() || dialog_photo.big_file_id.is_valid(); @@ -78,50 +65,6 @@ void parse(ProfilePhoto &profile_photo, ParserT &parser) { parse(profile_photo.id, parser); } -template -void store(const PhotoSize &photo_size, StorerT &storer) { - LOG(DEBUG) << "Store photo size " << photo_size; - store(photo_size.type, storer); - store(photo_size.dimensions, storer); - store(photo_size.size, storer); - store(photo_size.file_id, storer); - store(photo_size.progressive_sizes, storer); -} - -template -void parse(PhotoSize &photo_size, ParserT &parser) { - parse(photo_size.type, parser); - parse(photo_size.dimensions, parser); - parse(photo_size.size, parser); - parse(photo_size.file_id, parser); - if (parser.version() >= static_cast(Version::AddPhotoProgressiveSizes)) { - parse(photo_size.progressive_sizes, parser); - } else { - photo_size.progressive_sizes.clear(); - } - if (photo_size.type < 0 || photo_size.type >= 128) { - parser.set_error("Wrong PhotoSize type"); - return; - } - LOG(DEBUG) << "Parsed photo size " << photo_size; -} - -template -void store(const AnimationSize &animation_size, StorerT &storer) { - store(static_cast(animation_size), storer); - store(animation_size.main_frame_timestamp, storer); -} - -template -void parse(AnimationSize &animation_size, ParserT &parser) { - parse(static_cast(animation_size), parser); - if (parser.version() >= static_cast(Version::AddDialogPhotoHasAnimation)) { - parse(animation_size.main_frame_timestamp, parser); - } else { - animation_size.main_frame_timestamp = 0; - } -} - template void store(const Photo &photo, StorerT &storer) { bool has_minithumbnail = !photo.minithumbnail.empty(); diff --git a/td/telegram/PhotoSize.cpp b/td/telegram/PhotoSize.cpp new file mode 100644 index 000000000..375cf5714 --- /dev/null +++ b/td/telegram/PhotoSize.cpp @@ -0,0 +1,446 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/PhotoSize.h" + +#include "td/telegram/files/FileLocation.h" +#include "td/telegram/files/FileManager.h" + +#include "td/utils/base64.h" +#include "td/utils/HttpUrl.h" +#include "td/utils/logging.h" +#include "td/utils/misc.h" +#include "td/utils/Random.h" +#include "td/utils/SliceBuilder.h" + +#include +#include + +namespace td { + +static uint16 get_dimension(int32 size, const char *source) { + if (size < 0 || size > 65535) { + LOG(ERROR) << "Wrong image dimension = " << size << " from " << source; + return 0; + } + return narrow_cast(size); +} + +Dimensions get_dimensions(int32 width, int32 height, const char *source) { + Dimensions result; + result.width = get_dimension(width, source); + result.height = get_dimension(height, source); + if (result.width == 0 || result.height == 0) { + result.width = 0; + result.height = 0; + } + return result; +} + +static uint32 get_pixel_count(const Dimensions &dimensions) { + return static_cast(dimensions.width) * static_cast(dimensions.height); +} + +bool operator==(const Dimensions &lhs, const Dimensions &rhs) { + return lhs.width == rhs.width && lhs.height == rhs.height; +} + +bool operator!=(const Dimensions &lhs, const Dimensions &rhs) { + return !(lhs == rhs); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const Dimensions &dimensions) { + return string_builder << "(" << dimensions.width << ", " << dimensions.height << ")"; +} + +td_api::object_ptr get_minithumbnail_object(const string &packed) { + if (packed.size() < 3) { + return nullptr; + } + if (packed[0] == '\x01') { + static const string header = + base64_decode( + "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDACgcHiMeGSgjISMtKygwPGRBPDc3PHtYXUlkkYCZlo+AjIqgtObDoKrarYqMyP/L2u71////" + "m8H///" + "/6/+b9//j/2wBDASstLTw1PHZBQXb4pYyl+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj/" + "wAARCAAAAAADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/" + "8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0R" + "FRkd" + "ISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2" + "uHi4" + "+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/" + "8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkN" + "ERUZ" + "HSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2" + "Nna4" + "uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwA=") + .move_as_ok(); + static const string footer = base64_decode("/9k=").move_as_ok(); + auto result = td_api::make_object(); + result->height_ = static_cast(packed[1]); + result->width_ = static_cast(packed[2]); + result->data_ = PSTRING() << header.substr(0, 164) << packed[1] << header[165] << packed[2] << header.substr(167) + << packed.substr(3) << footer; + return result; + } + return nullptr; +} + +static td_api::object_ptr get_thumbnail_format_object(PhotoFormat format) { + switch (format) { + case PhotoFormat::Jpeg: + return td_api::make_object(); + case PhotoFormat::Png: + return td_api::make_object(); + case PhotoFormat::Webp: + return td_api::make_object(); + case PhotoFormat::Gif: + return td_api::make_object(); + case PhotoFormat::Tgs: + return td_api::make_object(); + case PhotoFormat::Mpeg4: + return td_api::make_object(); + case PhotoFormat::Webm: + return td_api::make_object(); + default: + UNREACHABLE(); + return nullptr; + } +} + +static StringBuilder &operator<<(StringBuilder &string_builder, PhotoFormat format) { + switch (format) { + case PhotoFormat::Jpeg: + return string_builder << "jpg"; + case PhotoFormat::Png: + return string_builder << "png"; + case PhotoFormat::Webp: + return string_builder << "webp"; + case PhotoFormat::Gif: + return string_builder << "gif"; + case PhotoFormat::Tgs: + return string_builder << "tgs"; + case PhotoFormat::Mpeg4: + return string_builder << "mp4"; + case PhotoFormat::Webm: + return string_builder << "webm"; + default: + UNREACHABLE(); + return string_builder; + } +} + +FileId register_photo_size(FileManager *file_manager, const PhotoSizeSource &source, int64 id, int64 access_hash, + string file_reference, DialogId owner_dialog_id, int32 file_size, DcId dc_id, + PhotoFormat format) { + LOG(DEBUG) << "Receive " << format << " photo " << id << " of type " << source.get_file_type("register_photo_size") + << " from " << dc_id; + auto suggested_name = PSTRING() << source.get_unique_name(id) << '.' << format; + auto file_location_source = owner_dialog_id.get_type() == DialogType::SecretChat ? FileLocationSource::FromUser + : FileLocationSource::FromServer; + return file_manager->register_remote( + FullRemoteFileLocation(source, id, access_hash, dc_id, std::move(file_reference)), file_location_source, + owner_dialog_id, file_size, 0, std::move(suggested_name)); +} + +PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice bytes, DialogId owner_dialog_id, + int32 width, int32 height) { + if (bytes.empty()) { + return PhotoSize(); + } + PhotoSize res; + res.type = 't'; + res.dimensions = get_dimensions(width, height, "get_secret_thumbnail_photo_size"); + res.size = narrow_cast(bytes.size()); + + // generate some random remote location to save + auto dc_id = DcId::invalid(); + auto photo_id = -(Random::secure_int64() & std::numeric_limits::max()); + + res.file_id = file_manager->register_remote( + FullRemoteFileLocation(PhotoSizeSource::thumbnail(FileType::EncryptedThumbnail, 't'), photo_id, 0, dc_id, + string()), + FileLocationSource::FromServer, owner_dialog_id, res.size, 0, + PSTRING() << static_cast(photo_id) << ".jpg"); + file_manager->set_content(res.file_id, std::move(bytes)); + + return res; +} + +Variant get_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id, + int64 access_hash, std::string file_reference, DcId dc_id, + DialogId owner_dialog_id, tl_object_ptr &&size_ptr, + PhotoFormat format) { + CHECK(size_ptr != nullptr); + + string type; + PhotoSize res; + BufferSlice content; + switch (size_ptr->get_id()) { + case telegram_api::photoSizeEmpty::ID: + return std::move(res); + case telegram_api::photoSize::ID: { + auto size = move_tl_object_as(size_ptr); + + type = std::move(size->type_); + res.dimensions = get_dimensions(size->w_, size->h_, "photoSize"); + res.size = size->size_; + + break; + } + case telegram_api::photoCachedSize::ID: { + auto size = move_tl_object_as(size_ptr); + + type = std::move(size->type_); + CHECK(size->bytes_.size() <= static_cast(std::numeric_limits::max())); + res.dimensions = get_dimensions(size->w_, size->h_, "photoCachedSize"); + res.size = static_cast(size->bytes_.size()); + + content = std::move(size->bytes_); + + break; + } + case telegram_api::photoStrippedSize::ID: { + auto size = move_tl_object_as(size_ptr); + if (format != PhotoFormat::Jpeg) { + LOG(ERROR) << "Receive unexpected JPEG minithumbnail in photo " << id << " from " << source << " of format " + << format; + return std::move(res); + } + return size->bytes_.as_slice().str(); + } + case telegram_api::photoSizeProgressive::ID: { + auto size = move_tl_object_as(size_ptr); + + if (size->sizes_.empty()) { + LOG(ERROR) << "Receive photo " << id << " from " << source << " with empty size " << to_string(size); + return std::move(res); + } + std::sort(size->sizes_.begin(), size->sizes_.end()); + + type = std::move(size->type_); + res.dimensions = get_dimensions(size->w_, size->h_, "photoSizeProgressive"); + res.size = size->sizes_.back(); + size->sizes_.pop_back(); + res.progressive_sizes = std::move(size->sizes_); + + break; + } + case telegram_api::photoPathSize::ID: { + auto size = move_tl_object_as(size_ptr); + if (format != PhotoFormat::Tgs && format != PhotoFormat::Webp && format != PhotoFormat::Webm) { + LOG(ERROR) << "Receive unexpected SVG minithumbnail in photo " << id << " from " << source << " of format " + << format; + return std::move(res); + } + return size->bytes_.as_slice().str(); + } + default: + UNREACHABLE(); + break; + } + + if (type.size() != 1) { + LOG(ERROR) << "Wrong photoSize \"" << type << "\" " << res; + res.type = 0; + } else { + res.type = static_cast(type[0]); + if (res.type >= 128) { + LOG(ERROR) << "Wrong photoSize \"" << type << "\" " << res; + res.type = 0; + } + } + if (source.get_type("get_photo_size") == PhotoSizeSource::Type::Thumbnail) { + source.thumbnail().thumbnail_type = res.type; + } + + res.file_id = register_photo_size(file_manager, source, id, access_hash, std::move(file_reference), owner_dialog_id, + res.size, dc_id, format); + + if (!content.empty()) { + file_manager->set_content(res.file_id, std::move(content)); + } + + return std::move(res); +} + +AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash, + std::string file_reference, DcId dc_id, DialogId owner_dialog_id, + tl_object_ptr &&size) { + CHECK(size != nullptr); + AnimationSize res; + if (size->type_ != "v" && size->type_ != "u") { + LOG(ERROR) << "Wrong videoSize \"" << size->type_ << "\" in " << to_string(size); + } + res.type = static_cast(size->type_[0]); + if (res.type >= 128) { + LOG(ERROR) << "Wrong videoSize \"" << res.type << "\" " << res; + res.type = 0; + } + res.dimensions = get_dimensions(size->w_, size->h_, "get_animation_size"); + res.size = size->size_; + if ((size->flags_ & telegram_api::videoSize::VIDEO_START_TS_MASK) != 0) { + res.main_frame_timestamp = size->video_start_ts_; + } + + if (source.get_type("get_animation_size") == PhotoSizeSource::Type::Thumbnail) { + source.thumbnail().thumbnail_type = res.type; + } + + res.file_id = register_photo_size(file_manager, source, id, access_hash, std::move(file_reference), owner_dialog_id, + res.size, dc_id, PhotoFormat::Mpeg4); + return res; +} + +PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id, + tl_object_ptr web_document_ptr) { + if (web_document_ptr == nullptr) { + return {}; + } + + FileId file_id; + vector> attributes; + int32 size = 0; + string mime_type; + switch (web_document_ptr->get_id()) { + case telegram_api::webDocument::ID: { + auto web_document = move_tl_object_as(web_document_ptr); + auto r_http_url = parse_url(web_document->url_); + if (r_http_url.is_error()) { + LOG(ERROR) << "Can't parse URL " << web_document->url_; + return {}; + } + auto http_url = r_http_url.move_as_ok(); + auto url = http_url.get_url(); + file_id = file_manager->register_remote(FullRemoteFileLocation(file_type, url, web_document->access_hash_), + FileLocationSource::FromServer, owner_dialog_id, 0, web_document->size_, + get_url_query_file_name(http_url.query_)); + size = web_document->size_; + mime_type = std::move(web_document->mime_type_); + attributes = std::move(web_document->attributes_); + break; + } + case telegram_api::webDocumentNoProxy::ID: { + auto web_document = move_tl_object_as(web_document_ptr); + if (web_document->url_.find('.') == string::npos) { + LOG(ERROR) << "Receive invalid URL " << web_document->url_; + return {}; + } + + auto r_file_id = file_manager->from_persistent_id(web_document->url_, file_type); + if (r_file_id.is_error()) { + LOG(ERROR) << "Can't register URL: " << r_file_id.error(); + return {}; + } + file_id = r_file_id.move_as_ok(); + + size = web_document->size_; + mime_type = std::move(web_document->mime_type_); + attributes = std::move(web_document->attributes_); + break; + } + default: + UNREACHABLE(); + } + CHECK(file_id.is_valid()); + bool is_animation = mime_type == "video/mp4"; + bool is_gif = mime_type == "image/gif"; + + Dimensions dimensions; + for (auto &attribute : attributes) { + switch (attribute->get_id()) { + case telegram_api::documentAttributeImageSize::ID: { + auto image_size = move_tl_object_as(attribute); + dimensions = get_dimensions(image_size->w_, image_size->h_, "web documentAttributeImageSize"); + break; + } + case telegram_api::documentAttributeAnimated::ID: + case telegram_api::documentAttributeHasStickers::ID: + case telegram_api::documentAttributeSticker::ID: + case telegram_api::documentAttributeVideo::ID: + case telegram_api::documentAttributeAudio::ID: + LOG(ERROR) << "Unexpected web document attribute " << to_string(attribute); + break; + case telegram_api::documentAttributeFilename::ID: + break; + default: + UNREACHABLE(); + } + } + + PhotoSize s; + s.type = is_animation ? 'v' : (is_gif ? 'g' : (file_type == FileType::Thumbnail ? 't' : 'n')); + s.dimensions = dimensions; + s.size = size; + s.file_id = file_id; + return s; +} + +td_api::object_ptr get_thumbnail_object(FileManager *file_manager, const PhotoSize &photo_size, + PhotoFormat format) { + if (!photo_size.file_id.is_valid()) { + return nullptr; + } + + if (format == PhotoFormat::Jpeg && photo_size.type == 'g') { + format = PhotoFormat::Gif; + } + + return td_api::make_object(get_thumbnail_format_object(format), photo_size.dimensions.width, + photo_size.dimensions.height, + file_manager->get_file_object(photo_size.file_id)); +} + +bool operator==(const PhotoSize &lhs, const PhotoSize &rhs) { + return lhs.type == rhs.type && lhs.dimensions == rhs.dimensions && lhs.size == rhs.size && + lhs.file_id == rhs.file_id && lhs.progressive_sizes == rhs.progressive_sizes; +} + +bool operator!=(const PhotoSize &lhs, const PhotoSize &rhs) { + return !(lhs == rhs); +} + +bool operator<(const PhotoSize &lhs, const PhotoSize &rhs) { + if (lhs.size != rhs.size) { + return lhs.size < rhs.size; + } + auto lhs_pixels = get_pixel_count(lhs.dimensions); + auto rhs_pixels = get_pixel_count(rhs.dimensions); + if (lhs_pixels != rhs_pixels) { + return lhs_pixels < rhs_pixels; + } + int32 lhs_type = lhs.type == 't' ? -1 : lhs.type; + int32 rhs_type = rhs.type == 't' ? -1 : rhs.type; + if (lhs_type != rhs_type) { + return lhs_type < rhs_type; + } + if (lhs.file_id != rhs.file_id) { + return lhs.file_id.get() < rhs.file_id.get(); + } + return lhs.dimensions.width < rhs.dimensions.width; +} + +StringBuilder &operator<<(StringBuilder &string_builder, const PhotoSize &photo_size) { + return string_builder << "{type = " << photo_size.type << ", dimensions = " << photo_size.dimensions + << ", size = " << photo_size.size << ", file_id = " << photo_size.file_id + << ", progressive_sizes = " << photo_size.progressive_sizes << "}"; +} + +bool operator==(const AnimationSize &lhs, const AnimationSize &rhs) { + return static_cast(lhs) == static_cast(rhs) && + fabs(lhs.main_frame_timestamp - rhs.main_frame_timestamp) < 1e-3; +} + +bool operator!=(const AnimationSize &lhs, const AnimationSize &rhs) { + return !(lhs == rhs); +} + +StringBuilder &operator<<(StringBuilder &string_builder, const AnimationSize &animation_size) { + return string_builder << static_cast(animation_size) << " from " + << animation_size.main_frame_timestamp; +} + +} // namespace td diff --git a/td/telegram/PhotoSize.h b/td/telegram/PhotoSize.h new file mode 100644 index 000000000..d73903b43 --- /dev/null +++ b/td/telegram/PhotoSize.h @@ -0,0 +1,87 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/DialogId.h" +#include "td/telegram/files/FileId.h" +#include "td/telegram/files/FileType.h" +#include "td/telegram/net/DcId.h" +#include "td/telegram/PhotoFormat.h" +#include "td/telegram/PhotoSizeSource.h" +#include "td/telegram/td_api.h" +#include "td/telegram/telegram_api.h" + +#include "td/utils/buffer.h" +#include "td/utils/common.h" +#include "td/utils/StringBuilder.h" +#include "td/utils/Variant.h" + +namespace td { + +class FileManager; + +struct Dimensions { + uint16 width = 0; + uint16 height = 0; +}; + +struct PhotoSize { + int32 type = 0; + Dimensions dimensions; + int32 size = 0; + FileId file_id; + vector progressive_sizes; +}; + +struct AnimationSize final : public PhotoSize { + double main_frame_timestamp = 0.0; +}; + +Dimensions get_dimensions(int32 width, int32 height, const char *source); + +bool operator==(const Dimensions &lhs, const Dimensions &rhs); +bool operator!=(const Dimensions &lhs, const Dimensions &rhs); + +StringBuilder &operator<<(StringBuilder &string_builder, const Dimensions &dimensions); + +td_api::object_ptr get_minithumbnail_object(const string &packed); + +FileId register_photo_size(FileManager *file_manager, const PhotoSizeSource &source, int64 id, int64 access_hash, + string file_reference, DialogId owner_dialog_id, int32 file_size, DcId dc_id, + PhotoFormat format); + +PhotoSize get_secret_thumbnail_photo_size(FileManager *file_manager, BufferSlice bytes, DialogId owner_dialog_id, + int32 width, int32 height); + +Variant get_photo_size(FileManager *file_manager, PhotoSizeSource source, int64 id, + int64 access_hash, string file_reference, DcId dc_id, + DialogId owner_dialog_id, tl_object_ptr &&size_ptr, + PhotoFormat format); + +AnimationSize get_animation_size(FileManager *file_manager, PhotoSizeSource source, int64 id, int64 access_hash, + string file_reference, DcId dc_id, DialogId owner_dialog_id, + tl_object_ptr &&size); + +PhotoSize get_web_document_photo_size(FileManager *file_manager, FileType file_type, DialogId owner_dialog_id, + tl_object_ptr web_document_ptr); + +td_api::object_ptr get_thumbnail_object(FileManager *file_manager, const PhotoSize &photo_size, + PhotoFormat format); + +bool operator==(const PhotoSize &lhs, const PhotoSize &rhs); +bool operator!=(const PhotoSize &lhs, const PhotoSize &rhs); + +bool operator<(const PhotoSize &lhs, const PhotoSize &rhs); + +StringBuilder &operator<<(StringBuilder &string_builder, const PhotoSize &photo_size); + +bool operator==(const AnimationSize &lhs, const AnimationSize &rhs); +bool operator!=(const AnimationSize &lhs, const AnimationSize &rhs); + +StringBuilder &operator<<(StringBuilder &string_builder, const AnimationSize &animation_size); + +} // namespace td diff --git a/td/telegram/PhotoSize.hpp b/td/telegram/PhotoSize.hpp new file mode 100644 index 000000000..c62bb35be --- /dev/null +++ b/td/telegram/PhotoSize.hpp @@ -0,0 +1,75 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#pragma once + +#include "td/telegram/files/FileId.hpp" +#include "td/telegram/PhotoSize.h" +#include "td/telegram/Version.h" + +#include "td/utils/logging.h" +#include "td/utils/tl_helpers.h" + +namespace td { + +template +void store(Dimensions dimensions, StorerT &storer) { + store(static_cast((static_cast(dimensions.width) << 16) | dimensions.height), storer); +} + +template +void parse(Dimensions &dimensions, ParserT &parser) { + uint32 width_height; + parse(width_height, parser); + dimensions.width = static_cast(width_height >> 16); + dimensions.height = static_cast(width_height & 0xFFFF); +} + +template +void store(const PhotoSize &photo_size, StorerT &storer) { + LOG(DEBUG) << "Store photo size " << photo_size; + store(photo_size.type, storer); + store(photo_size.dimensions, storer); + store(photo_size.size, storer); + store(photo_size.file_id, storer); + store(photo_size.progressive_sizes, storer); +} + +template +void parse(PhotoSize &photo_size, ParserT &parser) { + parse(photo_size.type, parser); + parse(photo_size.dimensions, parser); + parse(photo_size.size, parser); + parse(photo_size.file_id, parser); + if (parser.version() >= static_cast(Version::AddPhotoProgressiveSizes)) { + parse(photo_size.progressive_sizes, parser); + } else { + photo_size.progressive_sizes.clear(); + } + if (photo_size.type < 0 || photo_size.type >= 128) { + parser.set_error("Wrong PhotoSize type"); + return; + } + LOG(DEBUG) << "Parsed photo size " << photo_size; +} + +template +void store(const AnimationSize &animation_size, StorerT &storer) { + store(static_cast(animation_size), storer); + store(animation_size.main_frame_timestamp, storer); +} + +template +void parse(AnimationSize &animation_size, ParserT &parser) { + parse(static_cast(animation_size), parser); + if (parser.version() >= static_cast(Version::AddDialogPhotoHasAnimation)) { + parse(animation_size.main_frame_timestamp, parser); + } else { + animation_size.main_frame_timestamp = 0; + } +} + +} // namespace td diff --git a/td/telegram/PhotoSizeSource.cpp b/td/telegram/PhotoSizeSource.cpp index f2c2b3e2b..ff0085501 100644 --- a/td/telegram/PhotoSizeSource.cpp +++ b/td/telegram/PhotoSizeSource.cpp @@ -11,6 +11,7 @@ #include "td/telegram/UserId.h" #include "td/utils/common.h" +#include "td/utils/HttpUrl.h" #include "td/utils/Slice.h" #include "td/utils/SliceBuilder.h" #include "td/utils/StackAllocator.h" diff --git a/td/telegram/StickersManager.h b/td/telegram/StickersManager.h index 3a04431dd..cc73761c2 100644 --- a/td/telegram/StickersManager.h +++ b/td/telegram/StickersManager.h @@ -10,8 +10,8 @@ #include "td/telegram/files/FileId.h" #include "td/telegram/files/FileSourceId.h" #include "td/telegram/FullMessageId.h" -#include "td/telegram/Photo.h" #include "td/telegram/PhotoFormat.h" +#include "td/telegram/PhotoSize.h" #include "td/telegram/SecretInputMedia.h" #include "td/telegram/SpecialStickerSetType.h" #include "td/telegram/StickerFormat.h" diff --git a/td/telegram/StickersManager.hpp b/td/telegram/StickersManager.hpp index 822cdef4e..a0d02a9fa 100644 --- a/td/telegram/StickersManager.hpp +++ b/td/telegram/StickersManager.hpp @@ -10,7 +10,7 @@ #include "td/telegram/files/FileId.hpp" #include "td/telegram/misc.h" -#include "td/telegram/Photo.hpp" +#include "td/telegram/PhotoSize.hpp" #include "td/telegram/StickerFormat.h" #include "td/telegram/StickersManager.h" #include "td/telegram/Td.h" diff --git a/td/telegram/VideoNotesManager.h b/td/telegram/VideoNotesManager.h index 3693bff7f..944e6d0dd 100644 --- a/td/telegram/VideoNotesManager.h +++ b/td/telegram/VideoNotesManager.h @@ -7,7 +7,7 @@ #pragma once #include "td/telegram/files/FileId.h" -#include "td/telegram/Photo.h" +#include "td/telegram/PhotoSize.h" #include "td/telegram/SecretInputMedia.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" diff --git a/td/telegram/VideoNotesManager.hpp b/td/telegram/VideoNotesManager.hpp index 99d4df986..4df0bf0a1 100644 --- a/td/telegram/VideoNotesManager.hpp +++ b/td/telegram/VideoNotesManager.hpp @@ -9,7 +9,7 @@ #include "td/telegram/VideoNotesManager.h" #include "td/telegram/files/FileId.hpp" -#include "td/telegram/Photo.hpp" +#include "td/telegram/PhotoSize.hpp" #include "td/telegram/Version.h" #include "td/utils/common.h" diff --git a/td/telegram/VideosManager.h b/td/telegram/VideosManager.h index d519a3066..304b5e56e 100644 --- a/td/telegram/VideosManager.h +++ b/td/telegram/VideosManager.h @@ -7,7 +7,7 @@ #pragma once #include "td/telegram/files/FileId.h" -#include "td/telegram/Photo.h" +#include "td/telegram/PhotoSize.h" #include "td/telegram/SecretInputMedia.h" #include "td/telegram/td_api.h" #include "td/telegram/telegram_api.h" diff --git a/td/telegram/WebPageBlock.cpp b/td/telegram/WebPageBlock.cpp index 09b317670..a4abdee99 100644 --- a/td/telegram/WebPageBlock.cpp +++ b/td/telegram/WebPageBlock.cpp @@ -21,6 +21,7 @@ #include "td/telegram/Photo.h" #include "td/telegram/Photo.hpp" #include "td/telegram/PhotoFormat.h" +#include "td/telegram/PhotoSize.h" #include "td/telegram/Td.h" #include "td/telegram/Version.h" #include "td/telegram/VideosManager.h" diff --git a/td/telegram/WebPagesManager.cpp b/td/telegram/WebPagesManager.cpp index 75d18898f..67a5efbff 100644 --- a/td/telegram/WebPagesManager.cpp +++ b/td/telegram/WebPagesManager.cpp @@ -22,6 +22,7 @@ #include "td/telegram/MessagesManager.h" #include "td/telegram/Photo.h" #include "td/telegram/PhotoFormat.h" +#include "td/telegram/PhotoSize.h" #include "td/telegram/secret_api.h" #include "td/telegram/StickersManager.h" #include "td/telegram/Td.h"