@@ -125,6 +125,12 @@ void ChatOverlay::Draw(Bitmap& dst) {
125125 text_height = 12 ;
126126 }
127127
128+ for (auto it = emojiCache.begin (); it != emojiCache.end (); ++it) {
129+ if (auto emoji = it->second .lock ()) {
130+ emoji->in_viewport = false ;
131+ }
132+ }
133+
128134 int i = 0 ;
129135 int half_screen = y_end / 2 / text_height;
130136
@@ -205,6 +211,7 @@ void ChatOverlay::Draw(Bitmap& dst) {
205211 bool emojis_only = false ;
206212
207213 // begin override line height for components
214+ // if adding new components, remember to update LayoutViewport
208215
209216 // expand messages with only emojis
210217 if (std::any_of (line->cbegin (), line->cend (), [](const std::shared_ptr<ChatComponent>& comp) { return bool (comp->Downcast <ChatComponents::Screenshot>()); })) {
@@ -248,6 +255,7 @@ void ChatOverlay::Draw(Bitmap& dst) {
248255 offset += Text::Draw (*bitmap, offset, y + (baseline ? baseline - text_height : 0 ), font, str->color , str->string ).x ;
249256 }
250257 else if (auto emoji = span->Downcast <ChatComponents::Emoji>()) {
258+ emoji->in_viewport = true ;
251259 Point dims = emoji->GetSize ();
252260 if (emojis_only) dims.x = dims.y = line_height;
253261 int comp_y = y + (baseline ? baseline - text_height : 0 );
@@ -301,6 +309,44 @@ void ChatOverlay::Draw(Bitmap& dst) {
301309 dirty = false ;
302310}
303311
312+ ViewportInfo ChatOverlay::LayoutViewport (int text_height, const Font& font) const {
313+ Rect screen_rect = DisplayUi->GetScreenSurfaceRect ();
314+ int y_end = screen_rect.height ;
315+ int half_screen = y_end / 2 / text_height;
316+
317+ bool unlocked_start = scroll < half_screen;
318+ bool unlocked_end = scroll > messages.size () - half_screen;
319+ int last_sticky = std::max (0 , (int )messages.size () - half_screen * 2 );
320+ int viewport = show_all
321+ ? std::clamp (scroll - half_screen, 0 , std::min (scroll + half_screen, last_sticky))
322+ : 0 ;
323+
324+ int extra = 0 , lidx = viewport, scroll_extra = 0 , last_sticky_extra = 0 ;
325+ for (auto message = messages.rbegin () + viewport; message != messages.rend (); ++message) {
326+ const auto & components = message->text ;
327+ if (std::any_of (components.cbegin (), components.cend (), [](const std::shared_ptr<ChatComponent>& comp) { return bool (comp->Downcast <ChatComponents::Screenshot>()); })) {
328+ last_sticky_extra += extra = ChatScreenshot::sizer ().y - text_height;
329+ } else if (std::all_of (components.cbegin (), components.cend (), [](const std::shared_ptr<ChatComponent>& comp) { return bool (comp->Downcast <ChatComponents::Emoji>()); })) {
330+ last_sticky_extra += extra = (OverlayUtils::LargeScreen () ? 56 : 18 ) - text_height;
331+ } else {
332+ auto dims = Text::GetSize (font, message->text_orig );
333+ int lines = ceilf (dims.width / (double )bitmap->width ());
334+ last_sticky_extra += extra = std::max (0 , lines - 1 ) * text_height;
335+ lidx += std::max (0 , lines - 1 );
336+ }
337+ if (lidx <= scroll) scroll_extra += extra;
338+ if (lidx >= last_sticky) break ;
339+ lidx += 1 ;
340+ }
341+
342+ ViewportInfo out{};
343+ out.scroll_px = scroll * text_height + scroll_extra;
344+ out.last_sticky_px = last_sticky * text_height + last_sticky_extra;
345+ out.extra = last_sticky_extra;
346+
347+ return out;
348+ }
349+
304350ChatOverlayMessage& ChatOverlay::AddMessage (
305351 std::string_view message, std::string_view sender, std::string_view sender_uuid, std::string_view system, std::string_view badge,
306352 bool account, bool global, int rank) {
@@ -416,8 +462,8 @@ void ChatOverlay::UpdateScene() {
416462 if (Input::IsRawKeyTriggered (Input::Keys::RETURN ) && !input.empty ()) {
417463 // TODO: map chat and party chat
418464 std::string encoded = Utils::EncodeUTF (input);
419- ChatOverlay::AddMessage (encoded, " blah" , " 00000000000000000000" , " " , " " , true , true , 0 );
420- // GMI().sessionConn.SendPacket(Messages::C2S::SessionGSay { std::move(encoded) });
465+ // ChatOverlay::AddMessage(encoded, "blah", "00000000000000000000", "", "", true, true, 0);
466+ GMI ().sessionConn .SendPacket (Messages::C2S ::SessionSay { std::move (encoded) });
421467 input.clear ();
422468 dirty = true ;
423469 }
@@ -654,7 +700,7 @@ void ChatEmoji::RequestBitmap(ChatOverlay* parent_) {
654700}
655701
656702void ChatEmoji::DecodeGif (const std::string& filePath) {
657- constexpr int minimum_frame_delay = 64 ;
703+ constexpr int minimum_frame_delay = 20 ;
658704 frames.clear ();
659705 frameDelays.clear ();
660706 auto fs = FileFinder::OpenImage (" ../images/ynomoji" , filePath);
@@ -694,24 +740,21 @@ void ChatEmoji::DecodeGif(const std::string& filePath) {
694740}
695741
696742void ChatEmoji::DecodeWebP (const std::string& filePath) {
697- // Use a WebP decoding library to load the image
698- // Example: libwebp or similar library
699- // Pseudo-code:
700743 frames.clear ();
701744 frameDelays.clear ();
702745 auto fs = FileFinder::OpenImage (" ../images/ynomoji" , filePath);
703746 if (!fs) return ;
704747
705- auto dec = ImageWebP::Decoder::Create (fs);
748+ ImageWebP::Decoder dec (fs);
706749 if (!dec) return ;
707750
708751 ImageOut image{};
709752 TimingInfo timing{};
710753 int smear = 0 ;
711- while (dec.value (). ReadNext (image, timing)) {
712- auto bitmap = Bitmap::Create (image.pixels , image.width , image.height , image. bpp , format_R8G8B8A8_a ().format ());
754+ while (dec.ReadNext (image, timing)) {
755+ auto bitmap = Bitmap::Create (image.pixels , image.width , image.height , 0 , format_R8G8B8A8_a ().format ());
713756 if (!bitmap) {
714- delete image.pixels ;
757+ delete[] image.pixels ;
715758 continue ;
716759 }
717760 bitmap->SetBilinear ();
@@ -730,7 +773,6 @@ void ChatEmoji::DecodeWebP(const std::string& filePath) {
730773 }
731774 loopLength += frameDelays.back ();
732775 }
733- // if (image.pixels) delete image.pixels;
734776
735777 currentFrame = 0 ;
736778 if (loopLength && frames.size () > 1 ) {
@@ -754,7 +796,7 @@ void ChatEmoji::UpdateAnimation() {
754796 currentFrame = (currentFrame + 1 ) % frames.size ();
755797 lastFrameTime = now;
756798 bitmap = frames[currentFrame]; // Update the displayed frame
757- if (parent) parent->MarkDirty (); // Mark the parent as dirty to trigger a redraw
799+ if (parent && in_viewport ) parent->MarkDirty (); // Mark the parent as dirty to trigger a redraw
758800 }
759801}
760802
0 commit comments