Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
1ef8745
rustc index out of range
Murmele Nov 16, 2025
02e70ac
add possiblity to add a custom observer
Murmele Nov 22, 2025
118238a
get it compiled with a callback
Murmele Nov 27, 2025
d38feb3
fix using callback
Murmele Nov 27, 2025
ad938eb
rerun also for renderer_observer.h and map_observer.h changes
Murmele Nov 27, 2025
ed15ff6
enu must be in the same namespace than on the C++ side
Murmele Nov 28, 2025
3ac66dd
add comment
Murmele Nov 28, 2025
93afeb9
add also a map_observer and implement native Renderer and MapObserver…
Murmele Nov 29, 2025
ed79d06
add render_once function
Murmele Nov 29, 2025
a651b45
use trampoline objects to be able to use closures as callbacks
Murmele Nov 29, 2025
8aaf04d
Fix segfault
Murmele Nov 29, 2025
b0c9d6a
cleanup
Murmele Nov 29, 2025
83a0e80
remove example
Murmele Nov 30, 2025
4f54eb6
make all functions for the trampoline static
Murmele Nov 30, 2025
6b3dce2
set log level to trace
Murmele Nov 30, 2025
bc62d44
add function to read still image from the renderer
Murmele Nov 30, 2025
3be258d
add comment
Murmele Nov 30, 2025
fdd8855
add possibility to set the camera
Murmele Nov 30, 2025
0255123
add comment
Murmele Nov 30, 2025
b5e2245
implement interaction
Murmele Dec 2, 2025
b4832d4
add more
Murmele Dec 5, 2025
c46b4cf
rework VoidCallback
Murmele Dec 8, 2025
5804546
Use everywhere the same callback architecture like VoidCallback
Murmele Dec 8, 2025
90b05d7
cleanup
Murmele Dec 9, 2025
22223a2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 9, 2025
fa775b6
add comments
Murmele Dec 11, 2025
22ec522
Merge remote-tracking branch 'refs/remotes/origin/rust_compilation_bug'
Murmele Dec 11, 2025
5cc033d
Fix formatting in Cargo.toml dependencies section
nyurik Dec 11, 2025
1bc2c2f
Add more usage considerations and more meaningfull error messages
Murmele Dec 14, 2025
36095ed
align member variables
Murmele Dec 14, 2025
6921531
asign also finished_rendering_frame_callback
Murmele Dec 14, 2025
a33ccde
Remove RendererObserver, because it is not required
Murmele Dec 14, 2025
d6e767d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 14, 2025
b1d5e6b
revert setting map observer and callbacks during construction of the …
Murmele Dec 14, 2025
eec787e
Add documentation
Murmele Dec 14, 2025
2c584d4
Merge remote-tracking branch 'refs/remotes/origin/rust_compilation_bug'
Murmele Dec 14, 2025
3e71c22
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 14, 2025
abe0b92
try to simplify reading still image and make less copies
Murmele Dec 15, 2025
c31a3ed
remove old readStillImage
Murmele Dec 15, 2025
d09ed40
make read_still_image even easier
Murmele Dec 15, 2025
14c053a
Merge remote-tracking branch 'refs/remotes/origin/rust_compilation_bug'
Murmele Dec 15, 2025
70e5a23
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 15, 2025
58a0a3b
fix compilation
Murmele Dec 15, 2025
336987b
Merge remote-tracking branch 'refs/remotes/origin/rust_compilation_bug'
Murmele Dec 15, 2025
5cbd53e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 15, 2025
daa93e9
fix compilation
Murmele Dec 15, 2025
a5181fa
Merge remote-tracking branch 'refs/remotes/origin/rust_compilation_bug'
Murmele Dec 15, 2025
e498591
fix compilation
Murmele Dec 15, 2025
e48e370
add more documentation
Murmele Dec 21, 2025
4433966
add more documentation
Murmele Dec 21, 2025
4acaadf
fix deprecation warning
Murmele Dec 21, 2025
f40ab9f
add documentation and remove unsafe if not required
Murmele Dec 21, 2025
6926f4d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 21, 2025
7317c5e
add possibility to use non amalgam build library
Murmele Dec 22, 2025
4db6e80
fix lint problems
Murmele Dec 22, 2025
64668aa
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Dec 22, 2025
63d8e03
set also the size of the map
Murmele Dec 22, 2025
997c0e2
Merge remote-tracking branch 'refs/remotes/origin/rust_compilation_bug'
Murmele Dec 22, 2025
6993e5c
add Default ScreenCoordinate
Murmele Jan 7, 2026
6b267f7
fix clippy
Murmele Jan 7, 2026
dbec4ad
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jan 7, 2026
8de00e6
Merge branch 'main' into rust_compilation_bug
nyurik Jan 7, 2026
116ce8b
Merge branch 'main' into rust_compilation_bug
CommanderStorm Feb 15, 2026
0dbdad2
Merge remote-tracking branch 'refs/remotes/upstream/main'
Murmele Feb 17, 2026
7218e98
Merge branch 'main' into rust_compilation_bug
nyurik Feb 17, 2026
681c69b
Merge branch 'main' into rust_compilation_bug
nyurik Feb 17, 2026
47cd8f0
add slint example
Murmele Mar 1, 2026
10b6fed
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Mar 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ axum = "0.8"
clap = { version = "4.5.27", features = ["derive", "env", "unstable-markdown"] }
cmake = "0.1"
cxx = "1.0.138"
cxx-build = "1.0.138"
cxx-build = "1.0.138" # TODO: required to be a dependency and not only a build dependency?
downloader = "0.2.8"
env_logger = "0.11"
flate2 = "1.1.1"
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ We also support the following other features:
At its core, we work as follows:

```rust
use maplibre_native::{ImageRendererOptions, Image};
let mut renderer = ImageRendererOptions::new();
use maplibre_native::{ImageRendererBuilder, Image};
let mut renderer = ImageRendererBuilder::new();
renderer.with_size(512, 512);
let mut renderer = renderer.build_static_renderer();
renderer.load_style_from_url(&"https://demotiles.maplibre.org/style.json".parse().unwrap());
Expand Down
70 changes: 63 additions & 7 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
//! File for defining how we download and link against `MapLibre Native`.
//! Set `MLN_CORE_LIBRARY_PATH` and `MLN_CORE_LIBRARY_HEADERS_PATH` environment variables to use a local version of maplibre
//!
//! If you don't use the AMALGAM library define the env variable `MLN_CORE_LIBRARY_NO_AMALGAM` (value does not matter).
//! In this case all dependend libraries get linked manually
//!
//! IMPORTANT: The library path must point to the amalgan library which contains all the dependent libraries if `MLN_CORE_LIBRARY_NO_AMALGAM` is not set!
//!
//! Required libraries:
//! Fedora:
//! - `sudo dnf install libicu-devel libglslang-devel spirv-tools-devel libpng-devel libjpeg-turbo-devel libuv-devel libwebp-devel`

use std::path::{Path, PathBuf};
use std::{env, fs};
Expand Down Expand Up @@ -160,14 +170,22 @@ fn resolve_mln_core(root: &Path) -> (PathBuf, Vec<PathBuf>) {
};
assert!(
library_file.is_file(),
"The MLN library at {} must be a file",
"The MLN library at {} must be a file. When building locally on Linux it is called libmbgl-core.a",
library_file.display()
);
assert!(
headers.is_file(),
"The MLN headers at {} must be a zip file containing the headers",
headers.display()
);
if env::var_os("MLN_CORE_LIBRARY_HEADERS_PATH").is_some() {
assert!(
headers.is_file(),
"The MLN headers at {} must be a gzip (tar.gz) file containing the headers. When building locally checkout <maplibre-native repository>/.github/workflows/core-release.yml commands how to create the header archive",
headers.display()
);
} else {
assert!(
headers.is_file(),
"The MLN headers at {} must be a zip file containing the headers.",
headers.display()
);
}

let extracted_path = out_dir.join("headers");
extract_headers(&headers, &extracted_path);
Expand Down Expand Up @@ -199,6 +217,8 @@ fn resolve_mln_core(root: &Path) -> (PathBuf, Vec<PathBuf>) {
fn build_bridge(lib_name: &str, include_dirs: &[PathBuf]) {
println!("cargo:rerun-if-changed=src/renderer/bridge.rs");
println!("cargo:rerun-if-changed=include/map_renderer.h");
println!("cargo:rerun-if-changed=include/renderer_observer.h");
println!("cargo:rerun-if-changed=include/map_observer.h");
println!("cargo:rerun-if-changed=include/rust_log_observer.h");
cxx_build::bridge("src/renderer/bridge.rs")
.includes(include_dirs)
Expand All @@ -213,6 +233,10 @@ fn build_bridge(lib_name: &str, include_dirs: &[PathBuf]) {
fn build_mln() {
let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let (cpp_root, include_dirs) = resolve_mln_core(&root);

println!("cargo:rerun-if-env-changed=MLN_CORE_LIBRARY_NO_AMALGAM");
let no_amalgam_lib = env::var_os("MLN_CORE_LIBRARY_NO_AMALGAM").is_some();

println!(
"cargo:warning=Using precompiled maplibre-native static library from {}",
cpp_root.display()
Expand Down Expand Up @@ -261,7 +285,39 @@ fn build_mln() {
.replacen("lib", "", 1)
.replace(".a", "");
build_bridge(&lib_name, &include_dirs);

if no_amalgam_lib {
// The dependent libs are not bundled in the core lib, so we have to link manually
// Required for mlt-cpp. Cpp root link search was already added above
println!(
"cargo:rustc-link-search=native={}",
cpp_root
.parent()
.unwrap()
.join("vendor")
.join("maplibre-tile-spec")
.join("cpp")
.display()
);
println!("cargo:rustc-link-lib=mbgl-harfbuzz");
println!("cargo:rustc-link-lib=mbgl-freetype");
println!("cargo:rustc-link-lib=mbgl-vendor-nunicode");
println!("cargo:rustc-link-lib=mbgl-vendor-parsedate");
println!("cargo:rustc-link-lib=mbgl-vendor-sqlite");
println!("cargo:rustc-link-lib=mbgl-vendor-csscolorparser");
println!("cargo:rustc-link-lib=mlt-cpp"); // provided with matlibre-native
// println!("cargo:rustc-link-lib=utf8proc"); // sudo dnf install utf8proc-devel
println!("cargo:rustc-link-lib=icuuc"); //sudo dnf install libicu-devel
println!("cargo:rustc-link-lib=icudata"); //sudo dnf install libicu-devel
println!("cargo:rustc-link-lib=icui18n"); //sudo dnf install libicu-devel
println!("cargo:rustc-link-lib=glslang"); //sudo dnf install libglslang-devel
println!("cargo:rustc-link-lib=glslang-default-resource-limits"); //sudo dnf install libglslang-devel
println!("cargo:rustc-link-lib=SPIRV-Tools"); //sudo dnf install spirv-tools-devel // Required by glslang spirv-tools-devel
println!("cargo:rustc-link-lib=SPIRV-Tools-opt"); //sudo dnf install spirv-tools-devel // Required by glslang spirv-tools-devel
println!("cargo:rustc-link-lib=png"); // sudo dnf install libpng-devel
println!("cargo:rustc-link-lib=jpeg"); // sudo dnf install libjpeg-turbo-devel
println!("cargo:rustc-link-lib=uv"); // sudo dnf install libuv-devel
println!("cargo:rustc-link-lib=webp"); // sudo dnf install libwebp-devel
}
println!("cargo:rustc-link-lib=curl");
println!("cargo:rustc-link-lib=z");
match GraphicsRenderingAPI::from_selected_features() {
Expand Down
2 changes: 2 additions & 0 deletions examples/slint/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
maplibre_database.sqlite
11 changes: 11 additions & 0 deletions examples/slint/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "slint_map_libre_native"
version = "0.1.0"
edition = "2024"

[dependencies]
slint = { version = "1.15", features = ["backend-android-activity-06"] }
maplibre_native = { path = "../.."}

[build-dependencies]
slint-build = { version = "1.15" }
3 changes: 3 additions & 0 deletions examples/slint/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Slint Loves MapLibre

This example shows how MapLibre can be used in Slint.
3 changes: 3 additions & 0 deletions examples/slint/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
slint_build::compile("main.slint").expect("Slint build failed");
}
100 changes: 100 additions & 0 deletions examples/slint/main.slint
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { Button, VerticalBox, ComboBox, HorizontalBox, Slider } from "std-widgets.slint";
import { MaterialWindow, MaterialWindowAdapter, ListItem } from "dependencies/slint/ui-libraries/material/src/material.slint";


// Define a struct to hold window size
export struct Size {
width: length,
height: length,
}

// TODO: make this not global, because then it can be used only for one instance!
export global MapAdapter {
in-out property <image> map_texture;
callback tick_map_loop();
callback mouse_press(x: float, y: float);
callback mouse_release(y: float, x: float);
callback mouse_move(x: float, y: float, bool);
callback style_changed(style: string);
callback double_click_with_shift(x: float, y: float, shift_pressed: bool);
callback wheel_zoom(x: float, y: float, zoom_delta: float); // Positive delta_y: zoom out; Negative: zoom in
callback fly_to(location: string);
callback pitch_changed(int);
callback bearing_changed(float);
}

export component MapWidget inherits Rectangle {
// Default size
background: green;

// Expose map image area size so backend can match framebuffer size
out property<Size> map-size: { width: map.width, height: map.height };
callback map-size-changed(Size);
changed map-size => {
debug("Map size changed", map-size);
map-size-changed(map-size);
}

Timer {
interval: 16ms;
running: true;
triggered => {
MapAdapter.tick_map_loop();
}
}

VerticalBox {
spacing: 0px;

map := Image {
source: MapAdapter.map_texture;
image-fit: preserve; // Otherwise the mouse move is not correctly
vertical-alignment: center;
horizontal-alignment: center;

touch := TouchArea {
// Track last seen Shift modifier state from pointer events
property <bool> last-shift: false;
mouse-cursor: self.pressed ? grabbing : grab;

// Handle all pointer events properly
pointer-event(e) => {
self.last-shift = e.modifiers.shift;

if (e.kind == PointerEventKind.down) {
MapAdapter.mouse_press(self.mouse-x / 1px, self.mouse-y / 1px);
} else if (e.kind == PointerEventKind.up) {
MapAdapter.mouse_release(self.mouse-x / 1px, self.mouse-y / 1px);
} else if (e.kind == PointerEventKind.move && self.pressed) {
MapAdapter.mouse_move(self.mouse-x / 1px, self.mouse-y / 1px, true);
}
}

// Mouse-wheel / trackpad scroll to zoom
scroll-event(event) => {
MapAdapter.wheel_zoom(self.mouse-x / 1px,
self.mouse-y / 1px,
event.delta_y / 1px);
return accept;
}
}
}
}
}

export component MainWindow inherits MaterialWindow {
callback map-size-changed <=> map.map-size-changed;
out property <Size> map-size <=> map.map-size;

preferred-width: 800px;
preferred-height: 600px;

init => {
debug("MainWindow width: ", self.width, ", Height: ", self.height);
}

map:= MapWidget {
width: root.width;
height: root.height;
}
}
16 changes: 16 additions & 0 deletions examples/slint/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
mod maplibre;

use std::sync::Arc;

slint::include_modules!();

fn main() {
let ui = MainWindow::new().unwrap();

let size = ui.get_map_size();
let map = maplibre::create_map(size);

maplibre::init(&ui, &map);

ui.run().unwrap();
}
89 changes: 89 additions & 0 deletions examples/slint/src/maplibre.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use crate::MainWindow;
use crate::MapAdapter;
use maplibre_native::Height;
use maplibre_native::ScreenCoordinate;
use maplibre_native::Width;
use slint::ComponentHandle;
use std::sync::Arc;
mod headless;
use headless::MapLibre;
pub use headless::create_map;
use maplibre_native::{X, Y};
use std::cell::RefCell;

pub fn init(ui: &MainWindow, map: &Arc<RefCell<MapLibre>>) {
ui.on_map_size_changed({
let map = Arc::downgrade(map);
move |size| {
let size =
maplibre_native::Size::new(Width(size.width as u32), Height(size.height as u32));
map.upgrade()
.unwrap()
.borrow_mut()
.renderer()
.set_map_size(size);
}
});

ui.global::<MapAdapter>().on_tick_map_loop({
let map = Arc::downgrade(map);
let ui_handle = ui.as_weak();
move || {
let map = map.upgrade().unwrap();
let mut map = map.borrow_mut();
map.renderer().render_once();
if map.updated() {
let image = map.renderer().read_still_image();
let size = image.size();
let img = slint::SharedPixelBuffer::<slint::Rgba8Pixel>::clone_from_slice(
image.buffer(),
size.width(),
size.height(),
);
println!("New image: ({}, {})", size.width(), size.height());
ui_handle
.upgrade()
.unwrap()
.global::<MapAdapter>()
.set_map_texture(slint::Image::from_rgba8(img)); // TODO: check if the image really changed, otherwise we don't need to clone!
}
}
});

ui.global::<MapAdapter>().on_mouse_press({
let map = Arc::downgrade(map);
move |x: f32, y: f32| {
map.upgrade()
.unwrap()
.borrow_mut()
.set_position(ScreenCoordinate::new(X(x.into()), Y(y.into())));
}
});

ui.global::<MapAdapter>().on_mouse_move({
let map = Arc::downgrade(map);
move |x: f32, y: f32, _z: bool| {
println!("Mouse move");
let p = ScreenCoordinate::new(X(x.into()), Y(y.into()));
let map = map.upgrade().unwrap();
let mut map = map.borrow_mut();
let delta = p - map.position();
map.renderer().move_by(delta);
map.set_position(p);
}
});

ui.global::<MapAdapter>().on_wheel_zoom({
let map = Arc::downgrade(map);
move |x: f32, y: f32, delta: f32| {
const STEP: f64 = 1.2;
let pos = ScreenCoordinate::new(X(x.into()), Y(y.into()));
let scale = if delta > 0. { STEP } else { 1.0 / STEP };
map.upgrade()
.unwrap()
.borrow_mut()
.renderer()
.scale_by(scale, pos);
}
});
}
Loading
Loading