From 0ddc7d06664f5cffc85fa1b62e6fa6eb694fa41c Mon Sep 17 00:00:00 2001 From: David Alecrim <35930364+davidalecrim1@users.noreply.github.com> Date: Thu, 16 Apr 2026 04:44:00 -0300 Subject: [PATCH] Fix crash and enable GIF animation in markdown preview (#53459) Closes #53426 Fixes a crash when previewing markdown with GIFs, and enables GIF animation in the preview panel. Two issues: a partially-decoded GIF could crash the preview, and GIFs in markdown previews never animated. The fix for GIFs with empty comment extensions (`21 fe 00`) was implemented upstream in the `image-gif` crate (image-rs/image-gif#228) and released as `gif 0.14.2`. This PR bumps the dependency so those GIFs now render correctly in the markdown preview without any further changes to Zed itself. ## Screenshot Left=VS Code Right=Zed https://github.com/user-attachments/assets/7950abbc-1a79-4f01-a425-9595aa688325 Release Notes: - Fixed a crash in certain scenarios when opening Markdown Preview with GIFs. - Added GIF animation support for Markdown Preview. --------- Co-authored-by: Smit Barmase --- Cargo.lock | 223 +++++++++++++++++++------------- crates/gpui/src/assets.rs | 33 ++++- crates/gpui/src/elements/img.rs | 66 ++++++++-- crates/gpui/src/platform.rs | 20 ++- crates/markdown/src/markdown.rs | 2 +- typos.toml | 4 +- 6 files changed, 240 insertions(+), 108 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa6ed80b334d48d5d0be432f7df77e4435003cd3..ccf29c1b29318a785175b7f67b575ec95d8866c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -500,6 +500,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" +[[package]] +name = "aligned" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" +dependencies = [ + "as-slice", +] + [[package]] name = "aligned-vec" version = "0.6.4" @@ -734,6 +743,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "ascii" version = "1.1.0" @@ -1270,6 +1288,26 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "av-scenechange" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec", + "log", + "num-rational", + "num-traits", + "pastey", + "rayon", + "thiserror 2.0.17", + "v_frame", + "y4m", +] + [[package]] name = "av1-grain" version = "0.2.4" @@ -1930,7 +1968,7 @@ dependencies = [ "bitflags 2.10.0", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools 0.10.5", "log", "prettyplease", "proc-macro2", @@ -1950,7 +1988,7 @@ dependencies = [ "bitflags 2.10.0", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools 0.10.5", "proc-macro2", "quote", "regex", @@ -2012,9 +2050,12 @@ dependencies = [ [[package]] name = "bitstream-io" -version = "2.6.0" +version = "4.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" +checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757" +dependencies = [ + "core2", +] [[package]] name = "bitvec" @@ -2199,9 +2240,9 @@ dependencies = [ [[package]] name = "built" -version = "0.7.7" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" +checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64" [[package]] name = "bumpalo" @@ -2666,16 +2707,6 @@ dependencies = [ "nom 7.1.3", ] -[[package]] -name = "cfg-expr" -version = "0.15.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" -dependencies = [ - "smallvec", - "target-lexicon 0.12.16", -] - [[package]] name = "cfg-expr" version = "0.20.6" @@ -2683,7 +2714,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78cef5b5a1a6827c7322ae2a636368a573006b27cfa76c7ebd53e834daeaab6a" dependencies = [ "smallvec", - "target-lexicon 0.13.3", + "target-lexicon", ] [[package]] @@ -3765,6 +3796,15 @@ dependencies = [ "metal", ] +[[package]] +name = "core2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" +dependencies = [ + "memchr", +] + [[package]] name = "core_maths" version = "0.1.1" @@ -3943,7 +3983,7 @@ dependencies = [ "serde_derive", "sha2", "smallvec", - "target-lexicon 0.13.3", + "target-lexicon", "wasmtime-internal-math", ] @@ -3995,7 +4035,7 @@ dependencies = [ "cranelift-codegen", "log", "smallvec", - "target-lexicon 0.13.3", + "target-lexicon", ] [[package]] @@ -4012,7 +4052,7 @@ checksum = "976a3d85f197a56ae34ee4d5a5e469855ac52804a09a513d0562d425da0ff56e" dependencies = [ "cranelift-codegen", "libc", - "target-lexicon 0.13.3", + "target-lexicon", ] [[package]] @@ -5867,9 +5907,9 @@ dependencies = [ [[package]] name = "exr" -version = "1.73.0" +version = "1.74.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" +checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" dependencies = [ "bit_field", "half", @@ -7103,6 +7143,16 @@ dependencies = [ "weezl", ] +[[package]] +name = "gif" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.32.3" @@ -7123,7 +7173,7 @@ dependencies = [ "glib-sys", "gobject-sys", "libc", - "system-deps 7.0.7", + "system-deps", "windows-sys 0.61.2", ] @@ -7345,7 +7395,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d95e1a3a19ae464a7286e14af9a90683c64d70c02532d88d87ce95056af3e6c" dependencies = [ "libc", - "system-deps 7.0.7", + "system-deps", ] [[package]] @@ -7430,7 +7480,7 @@ checksum = "2dca35da0d19a18f4575f3cb99fe1c9e029a2941af5662f326f738a21edaf294" dependencies = [ "glib-sys", "libc", - "system-deps 7.0.7", + "system-deps", ] [[package]] @@ -8611,15 +8661,15 @@ dependencies = [ [[package]] name = "image" -version = "0.25.8" +version = "0.25.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" dependencies = [ "bytemuck", "byteorder-lite", "color_quant", "exr", - "gif", + "gif 0.14.2", "image-webp", "moxcms", "num-traits", @@ -8629,8 +8679,8 @@ dependencies = [ "rayon", "rgb", "tiff", - "zune-core", - "zune-jpeg", + "zune-core 0.5.1", + "zune-jpeg 0.5.15", ] [[package]] @@ -8697,7 +8747,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.15.5", "serde", "serde_core", ] @@ -8958,15 +9008,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.14.0" @@ -10708,9 +10749,9 @@ dependencies = [ [[package]] name = "moxcms" -version = "0.7.7" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c588e11a3082784af229e23e8e4ecf5bcc6fbe4f69101e0421ce8d79da7f0b40" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" dependencies = [ "num-traits", "pxfm", @@ -12033,6 +12074,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + [[package]] name = "pathdiff" version = "0.2.3" @@ -13544,8 +13591,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" dependencies = [ "bytes 1.11.1", - "heck 0.5.0", - "itertools 0.12.1", + "heck 0.4.1", + "itertools 0.10.5", "log", "multimap", "once_cell", @@ -13578,7 +13625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.10.5", "proc-macro2", "quote", "syn 2.0.117", @@ -14031,19 +14078,21 @@ checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" [[package]] name = "rav1e" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" dependencies = [ + "aligned-vec", "arbitrary", "arg_enum_proc_macro", "arrayvec", + "av-scenechange", "av1-grain", "bitstream-io", "built", "cfg-if", "interpolate_name", - "itertools 0.12.1", + "itertools 0.14.0", "libc", "libfuzzer-sys", "log", @@ -14052,23 +14101,21 @@ dependencies = [ "noop_proc_macro", "num-derive", "num-traits", - "once_cell", "paste", "profiling", - "rand 0.8.5", - "rand_chacha 0.3.1", + "rand 0.9.3", + "rand_chacha 0.9.0", "simd_helpers", - "system-deps 6.2.2", - "thiserror 1.0.69", + "thiserror 2.0.17", "v_frame", "wasm-bindgen", ] [[package]] name = "ravif" -version = "0.11.20" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" +checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" dependencies = [ "avif-serialize", "imgref", @@ -14656,7 +14703,7 @@ version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" dependencies = [ - "gif", + "gif 0.13.3", "image-webp", "log", "pico-args", @@ -14664,7 +14711,7 @@ dependencies = [ "svgtypes", "tiny-skia", "usvg", - "zune-jpeg", + "zune-jpeg 0.4.21", ] [[package]] @@ -16278,7 +16325,7 @@ version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1c97747dbf44bb1ca44a561ece23508e99cb592e862f22222dcf42f51d1e451" dependencies = [ - "heck 0.5.0", + "heck 0.4.1", "proc-macro2", "quote", "syn 2.0.117", @@ -17278,26 +17325,13 @@ dependencies = [ "libc", ] -[[package]] -name = "system-deps" -version = "6.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" -dependencies = [ - "cfg-expr 0.15.8", - "heck 0.5.0", - "pkg-config", - "toml 0.8.23", - "version-compare", -] - [[package]] name = "system-deps" version = "7.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c8f33736f986f16d69b6cb8b03f55ddcad5c41acc4ccc39dd88e84aa805e7f" dependencies = [ - "cfg-expr 0.20.6", + "cfg-expr", "heck 0.5.0", "pkg-config", "toml 0.9.8", @@ -17402,12 +17436,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" -[[package]] -name = "target-lexicon" -version = "0.12.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" - [[package]] name = "target-lexicon" version = "0.13.3" @@ -17760,16 +17788,16 @@ dependencies = [ [[package]] name = "tiff" -version = "0.10.3" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" +checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52" dependencies = [ "fax", "flate2", "half", "quick-error 2.0.1", "weezl", - "zune-jpeg", + "zune-jpeg 0.5.15", ] [[package]] @@ -19730,7 +19758,7 @@ dependencies = [ "serde", "serde_derive", "smallvec", - "target-lexicon 0.13.3", + "target-lexicon", "wasmparser 0.236.1", "wasmtime-environ", "wasmtime-internal-asm-macros", @@ -19781,7 +19809,7 @@ dependencies = [ "serde", "serde_derive", "smallvec", - "target-lexicon 0.13.3", + "target-lexicon", "wasm-encoder 0.236.1", "wasmparser 0.236.1", "wasmprinter", @@ -19847,7 +19875,7 @@ dependencies = [ "object", "pulley-interpreter", "smallvec", - "target-lexicon 0.13.3", + "target-lexicon", "thiserror 2.0.17", "wasmparser 0.236.1", "wasmtime-environ", @@ -19942,7 +19970,7 @@ dependencies = [ "cranelift-codegen", "gimli", "object", - "target-lexicon 0.13.3", + "target-lexicon", "wasmparser 0.236.1", "wasmtime-environ", "wasmtime-internal-cranelift", @@ -20544,7 +20572,7 @@ dependencies = [ "gimli", "regalloc2", "smallvec", - "target-lexicon 0.13.3", + "target-lexicon", "thiserror 2.0.17", "wasmparser 0.236.1", "wasmtime-environ", @@ -21959,6 +21987,12 @@ dependencies = [ "toml_edit 0.22.27", ] +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + [[package]] name = "yaml-rust2" version = "0.8.1" @@ -22743,6 +22777,12 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + [[package]] name = "zune-inflate" version = "0.2.54" @@ -22758,7 +22798,16 @@ version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" dependencies = [ - "zune-core", + "zune-core 0.4.12", +] + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core 0.5.1", ] [[package]] diff --git a/crates/gpui/src/assets.rs b/crates/gpui/src/assets.rs index 231a571ecde5d6cd8c4d6da89d375c0db0de0b17..cb8b47ed0166ba1bf67842766cd50cdd30e8a6d2 100644 --- a/crates/gpui/src/assets.rs +++ b/crates/gpui/src/assets.rs @@ -77,8 +77,13 @@ impl RenderImage { /// Get the size of this image, in pixels. pub fn size(&self, frame_index: usize) -> Size { - let (width, height) = self.data[frame_index].buffer().dimensions(); - size(width.into(), height.into()) + self.data + .get(frame_index) + .map(|frame| { + let (width, height) = frame.buffer().dimensions(); + size(width.into(), height.into()) + }) + .unwrap_or_default() } /// Get the size of this image, in pixels for display, adjusted for the scale factor. @@ -89,7 +94,10 @@ impl RenderImage { /// Get the delay of this frame from the previous pub fn delay(&self, frame_index: usize) -> Delay { - self.data[frame_index].delay() + self.data + .get(frame_index) + .map(|frame| frame.delay()) + .unwrap_or(Delay::from_numer_denom_ms(100, 1)) } /// Get the number of frames for this image. @@ -102,7 +110,24 @@ impl fmt::Debug for RenderImage { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ImageData") .field("id", &self.id) - .field("size", &self.size(0)) + .field("size", &self.data.first().map(|f| f.buffer().dimensions())) .finish() } } + +#[cfg(test)] +mod tests { + use super::*; + use smallvec::SmallVec; + + #[test] + fn empty_render_image_does_not_panic() { + let image = RenderImage::new(SmallVec::new()); + assert_eq!(image.frame_count(), 0); + assert_eq!(image.size(0), Size::default()); + assert_eq!(image.as_bytes(0), None); + assert_eq!(image.render_size(0), Size::default()); + assert_eq!(image.delay(0), Delay::from_numer_denom_ms(100, 1)); + let _ = format!("{image:?}"); + } +} diff --git a/crates/gpui/src/elements/img.rs b/crates/gpui/src/elements/img.rs index ccd4123048c22fda796ec3ae9d367209d4974c38..595052ac2103db55c310f3d55fdcc0da07c476ee 100644 --- a/crates/gpui/src/elements/img.rs +++ b/crates/gpui/src/elements/img.rs @@ -473,6 +473,9 @@ impl Element for Img { window, cx, ) { + if data.frame_count() == 0 { + return; + } let new_bounds = self .style .object_fit @@ -650,12 +653,26 @@ impl Asset for ImageAssetLoader { let mut frames = SmallVec::new(); for frame in decoder.into_frames() { - let mut frame = frame?; - // Convert from RGBA to BGRA. - for pixel in frame.buffer_mut().chunks_exact_mut(4) { - pixel.swap(0, 2); + match frame { + Ok(mut frame) => { + // Convert from RGBA to BGRA. + for pixel in frame.buffer_mut().chunks_exact_mut(4) { + pixel.swap(0, 2); + } + frames.push(frame); + } + Err(err) => { + log::debug!( + "Skipping GIF frame in {source:?} due to decode error: {err}" + ); + } } - frames.push(frame); + } + + if frames.is_empty() { + return Err(ImageCacheError::Other(Arc::new(anyhow::anyhow!( + "GIF could not be decoded: all frames failed ({source:?})" + )))); } frames @@ -668,12 +685,26 @@ impl Asset for ImageAssetLoader { let mut frames = SmallVec::new(); for frame in decoder.into_frames() { - let mut frame = frame?; - // Convert from RGBA to BGRA. - for pixel in frame.buffer_mut().chunks_exact_mut(4) { - pixel.swap(0, 2); + match frame { + Ok(mut frame) => { + // Convert from RGBA to BGRA. + for pixel in frame.buffer_mut().chunks_exact_mut(4) { + pixel.swap(0, 2); + } + frames.push(frame); + } + Err(err) => { + log::debug!( + "Skipping WebP frame in {source:?} due to decode error: {err}" + ); + } } - frames.push(frame); + } + + if frames.is_empty() { + return Err(ImageCacheError::Other(Arc::new(anyhow::anyhow!( + "WebP could not be decoded: all frames failed ({source:?})" + )))); } frames @@ -764,3 +795,18 @@ impl From for ImageCacheError { Self::Image(Arc::new(value)) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{TestAppContext, point, px, size}; + + #[gpui::test] + fn zero_frame_image_does_not_panic_on_paint(cx: &mut TestAppContext) { + let cx = cx.add_empty_window(); + let image = Arc::new(RenderImage::new(SmallVec::new())); + cx.draw(point(px(0.), px(0.)), size(px(100.), px(100.)), |_, _| { + img(ImageSource::Render(image)).into_any_element() + }); + } +} diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 5028915c968d07994c7496728d7a71ad593fe502..c67eb8f160f9db95c5150a056a3a9b787790426f 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -2100,12 +2100,22 @@ impl Image { let mut frames = SmallVec::new(); for frame in decoder.into_frames() { - let mut frame = frame?; - // Convert from RGBA to BGRA. - for pixel in frame.buffer_mut().chunks_exact_mut(4) { - pixel.swap(0, 2); + match frame { + Ok(mut frame) => { + // Convert from RGBA to BGRA. + for pixel in frame.buffer_mut().chunks_exact_mut(4) { + pixel.swap(0, 2); + } + frames.push(frame); + } + Err(err) => { + log::debug!("Skipping GIF frame due to decode error: {err}"); + } } - frames.push(frame); + } + + if frames.is_empty() { + anyhow::bail!("GIF could not be decoded: all frames failed"); } frames diff --git a/crates/markdown/src/markdown.rs b/crates/markdown/src/markdown.rs index a0f91cb43698be042207e2f51a5fc8cab16e67a7..5e1bb5729a81a914c1ef8a09d2c0abec7b52858a 100644 --- a/crates/markdown/src/markdown.rs +++ b/crates/markdown/src/markdown.rs @@ -1067,12 +1067,12 @@ impl MarkdownElement { image_container.child( img(source) + .id(("markdown-image", range.start)) .max_w_full() .when_some(height, |this, height| this.h(height)) .when_some(width, |this, width| this.w(width)), ) }); - let _ = range; } fn push_markdown_paragraph( diff --git a/typos.toml b/typos.toml index 959b5fc6f73477369572cdca3ff95d12b43f5ee1..f647c5ac91e1d57e410fe175ce8399348d838a54 100644 --- a/typos.toml +++ b/typos.toml @@ -94,6 +94,8 @@ extend-ignore-re = [ # "noet" is a vim variable (ideally to ignore locally) "noet", # Yarn Plug'n'Play - "PnP" + "PnP", + # `image` crate method: Delay::from_numer_denom_ms + "numer" ] check-filename = true