mod circle_layer;
mod fill_layer;
mod geojson;
mod geojson_source;
mod line_layer;
mod symbol_layer;
use image::DynamicImage;
use crate::renderer::bridge::ffi::Size;
use crate::ImageRenderer;
pub use circle_layer::CircleLayer;
pub use fill_layer::FillLayer;
pub use geojson::{GeoJson, GeoJsonError};
pub use geojson_source::GeoJsonSource;
pub use line_layer::LineLayer;
pub use symbol_layer::SymbolLayer;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Color {
r: f32,
g: f32,
b: f32,
a: f32,
}
impl Color {
#[must_use]
pub fn rgb(red: f32, green: f32, blue: f32) -> Self {
Self::rgba(red, green, blue, 1.0)
}
#[must_use]
pub fn rgba(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
assert!(
(0.0..=1.0).contains(&red)
&& (0.0..=1.0).contains(&green)
&& (0.0..=1.0).contains(&blue)
&& (0.0..=1.0).contains(&alpha),
"color channels must be in the 0.0..=1.0 range; got rgba({red}, {green}, {blue}, {alpha})",
);
Self { r: red * alpha, g: green * alpha, b: blue * alpha, a: alpha }
}
}
unsafe impl cxx::ExternType for Color {
type Id = cxx::type_id!("mbgl::Color");
type Kind = cxx::kind::Trivial;
}
pub trait StyleSourceRef {
fn source_id(&self) -> &str;
}
pub trait StyleImageRef {
fn image_id(&self) -> &str;
}
#[derive(Clone, Debug)]
pub struct SourceId(String);
impl SourceId {
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl StyleSourceRef for SourceId {
fn source_id(&self) -> &str {
self.as_str()
}
}
#[derive(Clone, Debug)]
pub struct ImageId(String);
impl ImageId {
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl StyleImageRef for ImageId {
fn image_id(&self) -> &str {
self.as_str()
}
}
#[non_exhaustive]
#[derive(Debug)]
pub enum StyleSource {
GeoJson(GeoJsonSource),
}
#[non_exhaustive]
#[derive(Debug)]
pub enum StyleLayer {
Circle(CircleLayer),
Fill(FillLayer),
Line(LineLayer),
Symbol(SymbolLayer),
}
#[derive(Debug)]
pub struct Style<'a, S> {
image_renderer: &'a mut ImageRenderer<S>,
}
impl<'a, S> Style<'a, S> {
pub fn get_ref(image_renderer: &'a mut ImageRenderer<S>) -> Self {
Self { image_renderer }
}
pub fn load_url(&mut self, url: &str) {
self.image_renderer.instance.pin_mut().style_load_from_url(url);
}
pub fn add_image(
&mut self,
id: &str,
image: &DynamicImage,
single_distance_field: bool,
) -> ImageId {
use image::EncodableLayout;
let image = image.to_rgba8();
self.image_renderer.instance.pin_mut().style_add_image(
id,
image.as_bytes(),
Size::new(super::Width(image.width()), super::Height(image.height())),
single_distance_field,
);
ImageId(id.to_owned())
}
pub fn remove_image(&mut self, id: &str) {
self.image_renderer.instance.pin_mut().style_remove_image(id);
}
pub fn add_source<T: Into<StyleSource>>(&mut self, source: T) -> SourceId {
match source.into() {
StyleSource::GeoJson(source) => {
let source_id = SourceId(source.source_id().to_owned());
self.image_renderer
.instance
.pin_mut()
.style_add_geojson_source(source.into_inner());
source_id
}
}
}
pub fn add_layer<T: Into<StyleLayer>>(&mut self, layer: T) {
match layer.into() {
StyleLayer::Circle(layer) => {
self.image_renderer.instance.pin_mut().style_add_circle_layer(layer.into_inner());
}
StyleLayer::Fill(layer) => {
self.image_renderer.instance.pin_mut().style_add_fill_layer(layer.into_inner());
}
StyleLayer::Line(layer) => {
self.image_renderer.instance.pin_mut().style_add_line_layer(layer.into_inner());
}
StyleLayer::Symbol(layer) => {
self.image_renderer.instance.pin_mut().style_add_symbol_layer(layer.into_inner());
}
}
}
}
#[cfg(test)]
mod tests {
use super::Color;
#[test]
fn rgba_stores_premultiplied_channels() {
assert_eq!(Color::rgba(1.0, 0.0, 0.0, 0.5), Color { r: 0.5, g: 0.0, b: 0.0, a: 0.5 });
}
#[test]
fn rgb_stores_opaque_channels() {
assert_eq!(Color::rgb(1.0, 0.0, 0.0), Color { r: 1.0, g: 0.0, b: 0.0, a: 1.0 });
}
}