Skip to content

Commit

Permalink
core, viewer: add support for WebP output
Browse files Browse the repository at this point in the history
WebP can be selected by passing `--image-format webp` on the command
line. For typical Minecraft worlds, this results in a size reduction of
10-15% without increasing processing time.
  • Loading branch information
neocturne committed Jan 11, 2025
1 parent bb11b29 commit c23b53a
Show file tree
Hide file tree
Showing 11 changed files with 81 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## [Unreleased] - ReleaseDate

### Added

- Added support for rendering tiles WebP format using the `--image-format` option

## [2.3.1] - 2025-01-06

### Fixed
Expand Down
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ enum-map = "2.7.3"
fastnbt = "2.3.2"
futures-util = "0.3.28"
git-version = "0.3.5"
image = { version = "0.25.1", default-features = false, features = ["png"] }
image = { version = "0.25.1", default-features = false, features = ["png", "webp"] }
indexmap = { version = "2.0.0", features = ["serde"] }
lru = "0.12.0"
minedmap-nbt = { version = "0.1.1", path = "crates/nbt", default-features = false }
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,18 @@ a proper webserver like [nginx](https://nginx.org/) or upload the viewer togethe
the generated map files to public webspace to make the map available to others.

If you are uploading the directory to a remote webserver, you do not need to upload the
`<viewer>/data/processed` directory, as that is only used locally to allow processing
`<viewer>/data/processed` directory, as it is only used locally to allow processing
updates more quickly.

### Image formats

MinedMap renders map tiles as PNG by default. Pass `--image-format webp` to select
WebP instead. For typical Minecraft worlds, using WebP reduces file sizes by 10-15%
without increasing processing time.

MinedMap always uses lossless compression for tile images, regardless of the
image format.

### Signs

![Sign screenshot](https://raw.githubusercontent.com/neocturne/MinedMap/e5d9c813ba3118d04dc7e52e3dc6f48808a69120/docs/images/signs.png)
Expand Down
31 changes: 30 additions & 1 deletion src/core/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::{
};

use anyhow::{Context, Result};
use clap::ValueEnum;
use indexmap::IndexSet;
use regex::{Regex, RegexSet};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -150,6 +151,8 @@ pub struct Config {
pub viewer_info_path: PathBuf,
/// Path of viewer entities file
pub viewer_entities_path: PathBuf,
/// Format of generated map tiles
pub image_format: ImageFormat,
/// Sign text filter patterns
pub sign_patterns: RegexSet,
/// Sign text transformation pattern
Expand Down Expand Up @@ -189,6 +192,7 @@ impl Config {
entities_path_final,
viewer_info_path,
viewer_entities_path,
image_format: args.image_format,
sign_patterns,
sign_transforms,
})
Expand Down Expand Up @@ -264,14 +268,39 @@ impl Config {
[&self.output_dir, Path::new(&dir)].iter().collect()
}

/// Returns the file extension for the configured image format
pub fn tile_extension(&self) -> &'static str {
match self.image_format {
ImageFormat::Png => "png",
ImageFormat::Webp => "webp",
}
}
/// Returns the configurured image format for the image library
pub fn tile_image_format(&self) -> image::ImageFormat {
match self.image_format {
ImageFormat::Png => image::ImageFormat::Png,
ImageFormat::Webp => image::ImageFormat::WebP,
}
}

/// Constructs the path of an output tile image
pub fn tile_path(&self, kind: TileKind, level: usize, coords: TileCoords) -> PathBuf {
let filename = coord_filename(coords, "png");
let filename = coord_filename(coords, self.tile_extension());
let dir = self.tile_dir(kind, level);
[Path::new(&dir), Path::new(&filename)].iter().collect()
}
}

/// Format of generated map tiles
#[derive(Debug, Clone, Copy, Default, ValueEnum)]
pub enum ImageFormat {
/// Generate PNG images
#[default]
Png,
/// Generate WebP images
Webp,
}

/// Copies a chunk image into a region tile
pub fn overlay_chunk<I, J>(image: &mut I, chunk: &J, coords: ChunkCoords)
where
Expand Down
3 changes: 3 additions & 0 deletions src/core/metadata_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ struct Metadata<'t> {
spawn: Spawn,
/// Enabled MinedMap features
features: Features,
/// Format of generated map tiles
tile_extension: &'static str,
}

/// Viewer entity JSON data structure
Expand Down Expand Up @@ -205,6 +207,7 @@ impl<'a> MetadataWriter<'a> {
mipmaps: Vec::new(),
spawn: Self::spawn(&level_dat),
features,
tile_extension: self.config.tile_extension(),
};

for tile_map in self.tiles.iter() {
Expand Down
5 changes: 4 additions & 1 deletion src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use anyhow::{Context, Result};
use clap::Parser;
use git_version::git_version;

use common::Config;
use common::{Config, ImageFormat};
use metadata_writer::MetadataWriter;
use region_processor::RegionProcessor;
use tile_mipmapper::TileMipmapper;
Expand Down Expand Up @@ -47,6 +47,9 @@ pub struct Args {
/// Enable verbose messages
#[arg(short, long)]
pub verbose: bool,
/// Format of generated map tiles
#[arg(long, value_enum, default_value_t)]
pub image_format: ImageFormat,
/// Prefix for text of signs to show on the map
#[arg(long)]
pub sign_prefix: Vec<String>,
Expand Down
5 changes: 4 additions & 1 deletion src/core/region_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ struct SingleRegionProcessor<'a> {
lightmap: image::GrayAlphaImage,
/// Processed entity intermediate data
entities: ProcessedEntities,
/// Format of generated map tiles
image_format: image::ImageFormat,
/// True if any unknown block or biome types were encountered during processing
has_unknown: bool,
}
Expand Down Expand Up @@ -127,6 +129,7 @@ impl<'a> SingleRegionProcessor<'a> {
processed_region,
lightmap,
entities,
image_format: processor.config.tile_image_format(),
has_unknown: false,
})
}
Expand Down Expand Up @@ -179,7 +182,7 @@ impl<'a> SingleRegionProcessor<'a> {
self.input_timestamp,
|file| {
self.lightmap
.write_to(file, image::ImageFormat::Png)
.write_to(file, self.image_format)
.context("Failed to save image")
},
)
Expand Down
2 changes: 1 addition & 1 deletion src/core/tile_mipmapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ where
}

image
.write_to(file, image::ImageFormat::Png)
.write_to(file, self.config.tile_image_format())
.context("Failed to save image")
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/core/tile_renderer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ impl<'a> TileRenderer<'a> {
processed_timestamp,
|file| {
image
.write_to(file, image::ImageFormat::Png)
.write_to(file, self.config.tile_image_format())
.context("Failed to save image")
},
)?;
Expand Down
10 changes: 6 additions & 4 deletions viewer/MinedMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function signIcon(material, kind) {
}

const MinedMapLayer = L.TileLayer.extend({
initialize: function (mipmaps, layer) {
initialize: function (mipmaps, layer, tile_extension) {
L.TileLayer.prototype.initialize.call(this, '', {
detectRetina: true,
tileSize: 512,
Expand All @@ -88,6 +88,7 @@ const MinedMapLayer = L.TileLayer.extend({

this.mipmaps = mipmaps;
this.layer = layer;
this.ext = tile_extension;
},

createTile: function (coords, done) {
Expand All @@ -112,7 +113,7 @@ const MinedMapLayer = L.TileLayer.extend({
return L.Util.emptyImageUrl;


return 'data/'+this.layer+'/'+z+'/r.'+coords.x+'.'+coords.y+'.png';
return `data/${this.layer}/${z}/r.${coords.x}.${coords.y}.${this.ext}`;
},
});

Expand Down Expand Up @@ -332,6 +333,7 @@ window.createMap = function () {
const res = await response.json();
const {mipmaps, spawn} = res;
const features = res.features || {};
const tile_extension = res.tile_extension || 'png';

const updateParams = function () {
const args = parseHash();
Expand Down Expand Up @@ -369,10 +371,10 @@ window.createMap = function () {

const overlayMaps = {};

const mapLayer = new MinedMapLayer(mipmaps, 'map');
const mapLayer = new MinedMapLayer(mipmaps, 'map', tile_extension);
mapLayer.addTo(map);

const lightLayer = new MinedMapLayer(mipmaps, 'light');
const lightLayer = new MinedMapLayer(mipmaps, 'light', tile_extension);
overlayMaps['Illumination'] = lightLayer;
if (params.light)
map.addLayer(lightLayer);
Expand Down

0 comments on commit c23b53a

Please sign in to comment.