feat(image-picker): animated-image badge on GIF/WebP thumbnails
Animated thumbnails now show a small play-arrow badge (bottom-right) so users can spot which images move before hovering; it's hidden while the image plays on hover. Detection is cheap — a new util::IsAnimatedImageFile probes without a full decode: animated WebP via WebPGetFeatures.has_animation, and a multi-frame GIF via a lightweight image-descriptor block walk (stops at the 2nd frame). The picker only probes .gif/.webp thumbnails and caches the result on the Thumb. Verified: animated GIF + animated WebP report animated; still GIF/WebP/PNG do not. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -258,6 +258,19 @@ public:
|
||||
dl->AddText(icoFont, cell*0.34f, ImVec2(cc.x - cell*0.17f, cc.y - cell*0.17f),
|
||||
OnSurfaceDisabled(), ICON_MD_IMAGE);
|
||||
}
|
||||
// "Animated" badge (bottom-right) on GIF/WebP that move — hidden while it plays on hover.
|
||||
if (t && t->animated && !hov) {
|
||||
float bh = std::max(13.0f * dp, cell * 0.24f);
|
||||
float bw = bh * 1.15f;
|
||||
ImVec2 bmax(mx.x - 4.0f * dp, mx.y - 4.0f * dp);
|
||||
ImVec2 bmin(bmax.x - bw, bmax.y - bh);
|
||||
dl->AddRectFilled(bmin, bmax, IM_COL32(0, 0, 0, 180), 4.0f * dp);
|
||||
float gsz = bh * 0.92f;
|
||||
ImVec2 gs = icoFont->CalcTextSizeA(gsz, FLT_MAX, 0, ICON_MD_PLAY_ARROW);
|
||||
dl->AddText(icoFont, gsz,
|
||||
ImVec2((bmin.x + bmax.x) * 0.5f - gs.x * 0.5f, (bmin.y + bmax.y) * 0.5f - gs.y * 0.5f),
|
||||
IM_COL32(255, 255, 255, 235), ICON_MD_PLAY_ARROW);
|
||||
}
|
||||
if (sel) dl->AddRect(mn, mx, WithAlpha(Primary(), 220), 6.0f * dp, 0, 2.0f * dp);
|
||||
else if (hov) { dl->AddRect(mn, mx, WithAlpha(OnSurface(), 90), 6.0f * dp, 0, 1.5f * dp); ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); }
|
||||
if (hov) material::Tooltip("%s", s_images[i].c_str());
|
||||
@@ -316,6 +329,7 @@ public:
|
||||
private:
|
||||
struct Thumb {
|
||||
ImTextureID tex = 0; int w = 0, h = 0; bool tried = false; // static frame-0 thumbnail
|
||||
bool animated = false; // multi-frame GIF/WebP (badge)
|
||||
std::vector<ImTextureID> frames; // full sequence (loaded on hover)
|
||||
std::vector<float> delays; int aw = 0, ah = 0; float totalDur = 0.0f; bool animTried = false;
|
||||
};
|
||||
@@ -376,6 +390,9 @@ private:
|
||||
if (s_loadedThisFrame >= kLoadsPerFrame) return nullptr; // defer to a later frame
|
||||
s_loadedThisFrame++;
|
||||
Thumb th; th.tried = true;
|
||||
// Cheap animation probe (header/structure only) so animated items can show a badge without
|
||||
// decoding all frames; only .gif/.webp can be animations in our decoders.
|
||||
if (isAnimatableName(absPath)) th.animated = util::IsAnimatedImageFile(absPath.c_str());
|
||||
int sw = 0, sh = 0;
|
||||
unsigned char* raw = util::LoadRawPixelsFromFile(absPath.c_str(), &sw, &sh);
|
||||
if (raw && sw > 0 && sh > 0) {
|
||||
|
||||
Reference in New Issue
Block a user