Submitted by: Xi Ruoyao Date: 2026-02-01 Initial Package Version: 2.0.7 Upstream Status: Applied for 2.1 beta but not 2.0.x. Origin: Upstream (see the cherry picked line for SHA), manually resolved the conflict and removed the XBM/XPM test because the test image would bloat this patch too much. Description: Add XBM and XPM support to allow loupe and gdk-pixbuf (>= 2.44.5) to load XBM and XPM images. From 3431a138c0bbe35069981faba69df11ff8addab9 Mon Sep 17 00:00:00 2001 From: Sophie Herold Date: Mon, 15 Dec 2025 13:47:12 +0100 Subject: [PATCH] image-rs: Add XBM and XPM support Closes #192 (cherry picked from commit e2eb88b7dacf468e8f386718f9b02c7cc5d22d92) --- .gitlab-ci.yml | 8 +++---- Cargo.lock | 10 ++++++++ Cargo.toml | 7 ++++-- docs/website/format-details.yml | 14 +++++++++++ docs/website/list-formats.rs | 3 +++ glycin-loaders/glycin-image-rs/Cargo.toml | 1 + .../glycin-image-rs/glycin-image-rs.conf | 6 +++++ glycin-loaders/glycin-image-rs/src/main.rs | 24 +++++++++++++++++++ glycin/src/api_common.rs | 5 +++- news.d/2.1.alpha/added-image-rs-xpm-support | 1 + tests/Cargo.toml | 1 - tests/encoding.rs | 4 ++-- 12 files changed, 74 insertions(+), 10 deletions(-) create mode 100644 news.d/2.1.alpha/added-image-rs-xpm-support diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fdcd4b6..75c923a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -47,7 +47,7 @@ variables: build-release-tarball: stage: build - image: rust:1.85-bookworm + image: rust:1.89-trixie extends: .install_dependencies script: - apt-get install -y gawk @@ -63,7 +63,7 @@ build-release-tarball: - if: $CI_COMMIT_TAG && $CI_COMMIT_REF_PROTECTED test-x86_64: - image: rust:1.85-bookworm + image: rust:1.89-trixie extends: .install_dependencies interruptible: true script: @@ -81,7 +81,7 @@ test-x86_64: test-i386: # Use hash to force i386, lookup "MANIFEST DIGEST" here - image: rust@sha256:a82436f09b89b68853e3a25c4acfd7b2d11d78be8ef35f5b4a75d3c927f37d7b + image: rust@sha256:97eee056216db51d76c0a47fbc9b14a7486d2e8e7e8904fb09969e394dcc5922 extends: .install_dependencies interruptible: true # As long as 32-bit CI is so unstable @@ -94,7 +94,7 @@ test-i386: - meson test -vC builddir test-aarch64: - image: rust:1.85-bookworm + image: rust:1.89-trixie tags: - asan-aarch64 extends: .install_dependencies diff --git a/Cargo.lock b/Cargo.lock index c192a99..5fa1fde 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1101,6 +1101,7 @@ dependencies = [ "gufo-exif", "gufo-jpeg", "image", + "image-extras", "jpeg-encoder", "log", "zune-jpeg", @@ -1390,6 +1391,15 @@ dependencies = [ "zune-jpeg", ] +[[package]] +name = "image-extras" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86d29ba92ef6970a2685cc758b455d190842b8b9e96c865ffd31cdb9954b7548" +dependencies = [ + "image", +] + [[package]] name = "image-webp" version = "0.2.4" diff --git a/Cargo.toml b/Cargo.toml index 2e3877b..b8404b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,8 +6,7 @@ license = "MPL-2.0 OR LGPL-2.1-or-later" homepage = "https://gitlab.gnome.org/GNOME/glycin" repository = "https://gitlab.gnome.org/GNOME/glycin" edition = "2021" -# Would be 1.83 without moxcms -rust-version = "1.85" +rust-version = "1.89" [profile.release] lto = true @@ -75,6 +74,10 @@ gufo-exif = { version = "0.3.0" } gufo-jpeg = { version = "0.3.0" } half = "2.4.1" image = { version = "0.25.7", default-features = false } +image-extras = { version = "0.1.0", default-features = false, features = [ + "xbm", + "xpm", +] } lcms2 = "6.0.3" lcms2-sys = "4.0.4" libc = "0.2.152" diff --git a/docs/website/format-details.yml b/docs/website/format-details.yml index 63bf020..20f2b05 100644 --- a/docs/website/format-details.yml +++ b/docs/website/format-details.yml @@ -165,3 +165,17 @@ image/x-win-bitmap: exif: unsupported xmp: unsupported animation: unsupported + +image/x-xbitmap: + icc: unsupported + cicp: unsupported + exif: unsupported + xmp: unsupported + animation: unsupported + +image/x-xpixmap: + icc: unsupported + cicp: unsupported + exif: unsupported + xmp: unsupported + animation: unsupported diff --git a/docs/website/list-formats.rs b/docs/website/list-formats.rs index 3ff749c..779195b 100755 --- a/docs/website/list-formats.rs +++ b/docs/website/list-formats.rs @@ -1,5 +1,8 @@ #!/usr/bin/env -S cargo +nightly -Zscript --- +[package] +edition = "2024" + [dependencies] glycin = { path = "../../glycin", features = ["unstable-config"] } glib = "0.21" diff --git a/glycin-loaders/glycin-image-rs/Cargo.toml b/glycin-loaders/glycin-image-rs/Cargo.toml index 8684c14..bded0d3 100644 --- a/glycin-loaders/glycin-image-rs/Cargo.toml +++ b/glycin-loaders/glycin-image-rs/Cargo.toml @@ -36,3 +36,4 @@ log.workspace = true jpeg-encoder = "0.6.0" # Force newer version for bugfixes zune-jpeg = "0.4.20" +image-extras.workspace = true diff --git a/glycin-loaders/glycin-image-rs/glycin-image-rs.conf b/glycin-loaders/glycin-image-rs/glycin-image-rs.conf index 34742b6..0af88a1 100644 --- a/glycin-loaders/glycin-image-rs/glycin-image-rs.conf +++ b/glycin-loaders/glycin-image-rs/glycin-image-rs.conf @@ -110,3 +110,9 @@ Exec = @EXEC@ [editor:image/qoi] Exec = @EXEC@ Creator = true + +[loader:image/x-xpixmap] +Exec = @EXEC@ + +[loader:image/x-xbitmap] +Exec = @EXEC@ diff --git a/glycin-loaders/glycin-image-rs/src/main.rs b/glycin-loaders/glycin-image-rs/src/main.rs index 3b84134..403aa2e 100644 --- a/glycin-loaders/glycin-image-rs/src/main.rs +++ b/glycin-loaders/glycin-image-rs/src/main.rs @@ -162,6 +162,8 @@ impl LoaderImplementation for ImgDecoder { mime_type: String, _details: InitializationDetails, ) -> Result<(Self, ImageDetails), ProcessError> { + image_extras::register(); + let mut buf = Vec::new(); stream.read_to_end(&mut buf).internal_error()?; let data = Cursor::new(buf); @@ -262,6 +264,8 @@ pub enum ImageRsDecoder { Tga(codecs::tga::TgaDecoder), Tiff(codecs::tiff::TiffDecoder), WebP(codecs::webp::WebPDecoder), + Xbm(image_extras::xbm::XbmDecoder), + Xpm(image_extras::xpm::XpmDecoder), } pub struct ImageRsFormat { @@ -362,6 +366,18 @@ impl ImageRsFormat { .format_name("WebP") .default_bit_depth(8) .supports_two_alpha_modes(true), + "image/x-xbitmap" => Self::new(ImageRsDecoder::Xbm( + image_extras::xbm::XbmDecoder::new(data).expected_error()?, + )) + .format_name("XBM") + .default_bit_depth(8) + .supports_two_alpha_modes(false), + "image/x-xpixmap" => Self::new(ImageRsDecoder::Xpm( + image_extras::xpm::XpmDecoder::new(data).expected_error()?, + )) + .format_name("XPM") + .default_bit_depth(8) + .supports_two_alpha_modes(false), mime_type => return Err(ProcessError::UnsupportedImageFormat(mime_type.to_string())), }) } @@ -414,6 +430,8 @@ impl<'a, T: std::io::BufRead + std::io::Seek + 'a> ImageRsFormat { ImageRsDecoder::Tga(ref mut d) => self.handler.info(d), ImageRsDecoder::Tiff(ref mut d) => self.handler.info(d), ImageRsDecoder::WebP(ref mut d) => self.handler.info(d), + ImageRsDecoder::Xbm(ref mut d) => self.handler.info(d), + ImageRsDecoder::Xpm(ref mut d) => self.handler.info(d), } } @@ -432,6 +450,8 @@ impl<'a, T: std::io::BufRead + std::io::Seek + 'a> ImageRsFormat { ImageRsDecoder::Tga(d) => self.handler.frame(d), ImageRsDecoder::Tiff(d) => self.handler.frame(d), ImageRsDecoder::WebP(d) => self.handler.frame(d), + ImageRsDecoder::Xbm(d) => self.handler.frame(d), + ImageRsDecoder::Xpm(d) => self.handler.frame(d), } } @@ -450,6 +470,8 @@ impl<'a, T: std::io::BufRead + std::io::Seek + 'a> ImageRsFormat { ImageRsDecoder::Tga(ref mut d) => self.handler.frame_details(d), ImageRsDecoder::Tiff(ref mut d) => self.handler.frame_details(d), ImageRsDecoder::WebP(ref mut d) => self.handler.frame_details(d), + ImageRsDecoder::Xbm(ref mut d) => self.handler.frame_details(d), + ImageRsDecoder::Xpm(ref mut d) => self.handler.frame_details(d), } } @@ -470,6 +492,8 @@ impl<'a, T: std::io::BufRead + std::io::Seek + 'a> ImageRsFormat { ImageRsDecoder::Tga(ref mut d) => d.set_limits(limits), ImageRsDecoder::Tiff(ref mut d) => d.set_limits(limits), ImageRsDecoder::WebP(ref mut d) => d.set_limits(limits), + ImageRsDecoder::Xbm(ref mut d) => d.set_limits(limits), + ImageRsDecoder::Xpm(ref mut d) => d.set_limits(limits), } } } diff --git a/glycin/src/api_common.rs b/glycin/src/api_common.rs index c904300..2215b52 100644 --- a/vendor/glycin/src/api_common.rs +++ b/vendor/glycin/src/api_common.rs @@ -315,7 +315,10 @@ pub(crate) async fn guess_mime_type(gfile_worker: &GFileWorker) -> Result