From 24ede2219728cee2ce8517c831105eced21901c0 Mon Sep 17 00:00:00 2001 From: Terrence Date: Fri, 6 Jun 2025 16:54:53 +0800 Subject: [PATCH] =?UTF-8?q?wechat=20ui=E6=94=AF=E6=8C=81=E6=91=84=E5=83=8F?= =?UTF-8?q?=E5=A4=B4=E9=A2=84=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/application.cc | 7 ++ main/display/lcd_display.cc | 163 ++++++++++++++++++++++++++++++------ 2 files changed, 143 insertions(+), 27 deletions(-) diff --git a/main/application.cc b/main/application.cc index 37bd2297..53aa8c3e 100644 --- a/main/application.cc +++ b/main/application.cc @@ -571,6 +571,13 @@ void Application::Start() { audio_processor_->Initialize(codec); audio_processor_->OnOutput([this](std::vector&& data) { + { + std::lock_guard lock(mutex_); + if (audio_send_queue_.size() >= MAX_AUDIO_PACKETS_IN_QUEUE) { + ESP_LOGW(TAG, "Too many audio packets in queue, drop the newest packet"); + return; + } + } background_task_->Schedule([this, data = std::move(data)]() mutable { opus_encoder_->Encode(std::move(data), [this](std::vector&& opus) { AudioStreamPacket packet; diff --git a/main/display/lcd_display.cc b/main/display/lcd_display.cc index f7944607..7ccee775 100644 --- a/main/display/lcd_display.cc +++ b/main/display/lcd_display.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include "assets/lang_config.h" #include #include "settings.h" @@ -599,6 +600,108 @@ void LcdDisplay::SetChatMessage(const char* role, const char* content) { // Store reference to the latest message label chat_message_label_ = msg_text; } + +void LcdDisplay::SetPreviewImage(const lv_img_dsc_t* img_dsc) { + DisplayLockGuard lock(this); + if (content_ == nullptr) { + return; + } + + if (img_dsc != nullptr) { + // Create a message bubble for image preview + lv_obj_t* img_bubble = lv_obj_create(content_); + lv_obj_set_style_radius(img_bubble, 8, 0); + lv_obj_set_scrollbar_mode(img_bubble, LV_SCROLLBAR_MODE_OFF); + lv_obj_set_style_border_width(img_bubble, 1, 0); + lv_obj_set_style_border_color(img_bubble, current_theme_.border, 0); + lv_obj_set_style_pad_all(img_bubble, 8, 0); + + // Set image bubble background color (similar to system message) + lv_obj_set_style_bg_color(img_bubble, current_theme_.assistant_bubble, 0); + + // 设置自定义属性标记气泡类型 + lv_obj_set_user_data(img_bubble, (void*)"image"); + + // Create the image object inside the bubble + lv_obj_t* preview_image = lv_image_create(img_bubble); + + // Copy the image descriptor and data to avoid source data changes + lv_img_dsc_t* copied_img_dsc = (lv_img_dsc_t*)heap_caps_malloc(sizeof(lv_img_dsc_t), MALLOC_CAP_8BIT); + if (copied_img_dsc == nullptr) { + ESP_LOGE(TAG, "Failed to allocate memory for image descriptor"); + lv_obj_del(img_bubble); + return; + } + + // Copy the header + copied_img_dsc->header = img_dsc->header; + copied_img_dsc->data_size = img_dsc->data_size; + + // Copy the image data + uint8_t* copied_data = (uint8_t*)heap_caps_malloc(img_dsc->data_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); + if (copied_data == nullptr) { + // Fallback to internal RAM if SPIRAM allocation fails + copied_data = (uint8_t*)heap_caps_malloc(img_dsc->data_size, MALLOC_CAP_8BIT); + } + if (copied_data == nullptr) { + ESP_LOGE(TAG, "Failed to allocate memory for image data (size: %lu bytes)", img_dsc->data_size); + heap_caps_free(copied_img_dsc); + lv_obj_del(img_bubble); + return; + } + + memcpy(copied_data, img_dsc->data, img_dsc->data_size); + copied_img_dsc->data = copied_data; + + // Calculate appropriate size for the image + lv_coord_t max_width = LV_HOR_RES * 70 / 100; // 70% of screen width + lv_coord_t max_height = LV_VER_RES * 50 / 100; // 50% of screen height + + // Calculate zoom factor to fit within maximum dimensions + lv_coord_t img_width = copied_img_dsc->header.w; + lv_coord_t img_height = copied_img_dsc->header.h; + + lv_coord_t zoom_w = (max_width * 256) / img_width; + lv_coord_t zoom_h = (max_height * 256) / img_height; + lv_coord_t zoom = (zoom_w < zoom_h) ? zoom_w : zoom_h; + + // Ensure zoom doesn't exceed 256 (100%) + if (zoom > 256) zoom = 256; + + // Set image properties + lv_image_set_src(preview_image, copied_img_dsc); + lv_image_set_scale(preview_image, zoom); + + // Add event handler to clean up copied data when image is deleted + lv_obj_add_event_cb(preview_image, [](lv_event_t* e) { + lv_img_dsc_t* copied_img_dsc = (lv_img_dsc_t*)lv_event_get_user_data(e); + if (copied_img_dsc != nullptr) { + heap_caps_free((void*)copied_img_dsc->data); + heap_caps_free(copied_img_dsc); + } + }, LV_EVENT_DELETE, (void*)copied_img_dsc); + + // Calculate actual scaled image dimensions + lv_coord_t scaled_width = (img_width * zoom) / 256; + lv_coord_t scaled_height = (img_height * zoom) / 256; + + // Set bubble size to be 16 pixels larger than the image (8 pixels on each side) + lv_obj_set_width(img_bubble, scaled_width + 16); + lv_obj_set_height(img_bubble, scaled_height + 16); + + // Don't grow in flex layout + lv_obj_set_style_flex_grow(img_bubble, 0, 0); + + // Center the image within the bubble + lv_obj_center(preview_image); + + // Left align the image bubble like assistant messages + lv_obj_align(img_bubble, LV_ALIGN_LEFT_MID, 0, 0); + + // Auto-scroll to the image bubble + lv_obj_scroll_to_view_recursive(img_bubble, LV_ANIM_ON); + } +} #else void LcdDisplay::SetupUI() { DisplayLockGuard lock(this); @@ -703,6 +806,31 @@ void LcdDisplay::SetupUI() { lv_obj_center(low_battery_label_); lv_obj_add_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN); } + +void LcdDisplay::SetPreviewImage(const lv_img_dsc_t* img_dsc) { + DisplayLockGuard lock(this); + if (preview_image_ == nullptr) { + return; + } + + if (img_dsc != nullptr) { + // zoom factor 0.5 + lv_image_set_scale(preview_image_, 128 * width_ / img_dsc->header.w); + // 设置图片源并显示预览图片 + lv_image_set_src(preview_image_, img_dsc); + lv_obj_clear_flag(preview_image_, LV_OBJ_FLAG_HIDDEN); + // 隐藏emotion_label_ + if (emotion_label_ != nullptr) { + lv_obj_add_flag(emotion_label_, LV_OBJ_FLAG_HIDDEN); + } + } else { + // 隐藏预览图片并显示emotion_label_ + lv_obj_add_flag(preview_image_, LV_OBJ_FLAG_HIDDEN); + if (emotion_label_ != nullptr) { + lv_obj_clear_flag(emotion_label_, LV_OBJ_FLAG_HIDDEN); + } + } +} #endif void LcdDisplay::SetEmotion(const char* emotion) { @@ -752,12 +880,14 @@ void LcdDisplay::SetEmotion(const char* emotion) { } else { lv_label_set_text(emotion_label_, "😶"); } - + +#if !CONFIG_USE_WECHAT_MESSAGE_STYLE // 显示emotion_label_,隐藏preview_image_ lv_obj_clear_flag(emotion_label_, LV_OBJ_FLAG_HIDDEN); if (preview_image_ != nullptr) { lv_obj_add_flag(preview_image_, LV_OBJ_FLAG_HIDDEN); } +#endif } void LcdDisplay::SetIcon(const char* icon) { @@ -767,37 +897,14 @@ void LcdDisplay::SetIcon(const char* icon) { } lv_obj_set_style_text_font(emotion_label_, &font_awesome_30_4, 0); lv_label_set_text(emotion_label_, icon); - + +#if !CONFIG_USE_WECHAT_MESSAGE_STYLE // 显示emotion_label_,隐藏preview_image_ lv_obj_clear_flag(emotion_label_, LV_OBJ_FLAG_HIDDEN); if (preview_image_ != nullptr) { lv_obj_add_flag(preview_image_, LV_OBJ_FLAG_HIDDEN); } -} - -void LcdDisplay::SetPreviewImage(const lv_img_dsc_t* img_dsc) { - DisplayLockGuard lock(this); - if (preview_image_ == nullptr) { - return; - } - - if (img_dsc != nullptr) { - // zoom factor 0.5 - lv_img_set_zoom(preview_image_, 128 * width_ / img_dsc->header.w); - // 设置图片源并显示预览图片 - lv_img_set_src(preview_image_, img_dsc); - lv_obj_clear_flag(preview_image_, LV_OBJ_FLAG_HIDDEN); - // 隐藏emotion_label_ - if (emotion_label_ != nullptr) { - lv_obj_add_flag(emotion_label_, LV_OBJ_FLAG_HIDDEN); - } - } else { - // 隐藏预览图片并显示emotion_label_ - lv_obj_add_flag(preview_image_, LV_OBJ_FLAG_HIDDEN); - if (emotion_label_ != nullptr) { - lv_obj_clear_flag(emotion_label_, LV_OBJ_FLAG_HIDDEN); - } - } +#endif } void LcdDisplay::SetTheme(const std::string& theme_name) { @@ -900,6 +1007,8 @@ void LcdDisplay::SetTheme(const std::string& theme_name) { lv_obj_set_style_bg_color(bubble, current_theme_.assistant_bubble, 0); } else if (strcmp(bubble_type, "system") == 0) { lv_obj_set_style_bg_color(bubble, current_theme_.system_bubble, 0); + } else if (strcmp(bubble_type, "image") == 0) { + lv_obj_set_style_bg_color(bubble, current_theme_.system_bubble, 0); } // Update border color