
Headless basemap tile rasterizer: accepts map styles and north-up XYZ tiles, performs bounded local CPU rendering, returns encoded PNG bytes, and surfaces typed exceptions with redacted diagnostics.
Rentile is a headless Kotlin Multiplatform basemap tile rasterizer. It accepts a supported map style and north-up XYZ tile identities, performs bounded local CPU rendering, and returns encoded PNG bytes without a UI view or platform render loop.
Rentile is published to the public repository at https://maven.rohittp.com. Every push to main that changes anything outside documentation publishes a new release, taking the highest version already public and advancing its patch component. Set VERSION_NAME in the root gradle.properties above every published version to cut a deliberate minor or major release instead. Releases cannot overwrite an existing coordinate.
0.15.1 upgrades Skiko and the bundled Android native libraries to 0.150.2. Consumers that
depend on Skiko directly must align with Rentile's strict version constraint. See the
0.15.1 migration guide.
0.15.0 adds opt-in temporary coverage without hillshade and reduces CPU hillshade sampling
work. Full-detail output remains the default, with unchanged cache keys. The appended
RenderOptions field changes constructor/copy ABI, so hosts must recompile. See the
0.15.0 migration guide.
0.13.0 added host scenery support. It adds opt-in host-owned extrusion
polygons and style evaluation, with selective decoding and per-operation memory limits. Existing
profiles retain their raster behavior. See the 0.13.0 migration guide
and ADR 0037. Publication is complete
only when the release workflow passes and all eight public coordinates resolve.
0.12.1 fixes line and line-center label text: it is laid out on one line, as Mapbox lays it
out, instead of wrapping at text-max-width like point text. No public signatures changed; the two
label-candidate keys moved. See the 0.12.1 migration guide.
0.12.0 is a deliberate breaking minor. It lets a host own every symbol layer, hands it the style's
sprite sheet, and bounds the label glyph atlas; RentileConfiguration, LabelCandidate and
LabelIconRef gain appended fields, so every consumer recompiles, and the two label-candidate keys
move. See the 0.12.0 migration guide.
0.11.4 is whole on all eight coordinates and a drop-in for 0.11.3: a filter on a raster,
hillshade or background layer is ignored instead of failing the style. See the
0.11.4 ledger and, for 0.11.3, the
0.11.3 migration guide. Consumers upgrading from 0.11.0 or
0.11.1 should also read the 0.11.2 guide, which carries the one change
an OkHttp-backed transport has to make for Rentile's per-origin budget to be spendable.
0.11.0 and 0.11.1 are both published without a kmp-macosarm64 coordinate, so a consumer with
that target resolves a 404 from either. A released coordinate is immutable; move to 0.11.2 or
later. See the 0.11.1 migration ledger.
arm64-v8a and x86_64
jvm
iosArm64, iosSimulatorArm64, and macosArm64
linuxX64 and linuxArm64
Apple support is Apple Silicon only; Rentile does not publish iosX64 or macosX64. See ADR 0022.
Rentile publishes one consumer coordinate:
commonMain.dependencies {
implementation("com.rohittp.rentile:kmp:<version>")
}Add the shared public repository before the standard repositories. No repository credentials are required. Rentile is an ordinary KMP dependency, so it resolves through dependencyResolutionManagement; this project does not publish a Gradle plugin. Skiko's platform artifacts still come from JetBrains' public Compose repository.
dependencyResolutionManagement {
repositories {
maven("https://maven.rohittp.com")
mavenCentral()
google()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") {
content { includeGroup("org.jetbrains.skiko") }
}
}
}python3 tools/check_coverage_manifest.py compatibility/rentile-v1-coverage.json
python3 tools/check_corpus_fidelity_policy.py compatibility/rentile-v1-coverage.json
./gradlew :kmp:checkKotlinAbi
./gradlew :kmp:testAndroidHostTest :kmp:jvmTest :kmp:macosArm64Test
./gradlew :kmp:compileKotlinIosArm64 :kmp:compileKotlinIosSimulatorArm64
./gradlew :kmp:compileKotlinLinuxX64 :kmp:compileKotlinLinuxArm64
./gradlew :kmp:publishAllPublicationsToLocalTestRepository
./gradlew -p consumer-smoke compileAndroidMain compileKotlinJvm compileKotlinIosArm64 compileKotlinIosSimulatorArm64 compileKotlinMacosArm64 compileKotlinLinuxX64 compileKotlinLinuxArm64
Merging a non-documentation change to main starts one publication automatically. Do not also
dispatch the workflow manually for the same commit: a second run can legitimately resolve the next
patch version. Use manual dispatch only when no push-triggered run exists. To dispatch deliberately,
or to watch the run a push started:
gh workflow run publish.yml --repo rohittp0/rentile --ref main
gh run list --repo rohittp0/rentile --workflow publish.yml --limit 1
gh run watch RUN_ID --repo rohittp0/rentile --exit-status
Architecture decisions and the evolving contract are in docs/. Public documentation is prepared for https://rohittp.com/rentile/.
The rolling corpus is discovered from the public paginated map catalog and checked against a credential-free Coverage Manifest. See compatibility/README.md for local and workflow usage.
Rentile does not bake text into Output Tiles. Under the default profile it admits every visible
text-bearing vector symbol layer to descriptor and candidate compilation (the host-owned-symbols
profile below admits icon-only layers too), including point, line, and line-center labels, and
carries each successfully resolved paired icon so the viewport-owning consumer can place and
collide the complete symbol. Unsupported scripts, constructs, sources, and feature values are
excluded with stable diagnostics instead of being represented as plausible but incorrect labels.
The public API exposes
glyph geometry, source-line geometry, feature-resolved paint, explicit overlap, alignment, and
z-order enums, and icon text-fit inputs. LabelIconRef now carries a LabelIconAnchor for the final
fitted box instead of the removed pre-fit anchorOffsetX/Y shifts. The consumer resolves sprite
imagery by LabelIconRef.imageName and still owns projection, shaping limitations, cross-tile
collision, depth, and final drawing. The label batch carries no sprite sheet; a consumer that draws
icons itself calls acquireSpriteAtlas for the style's sheet, at ratio 1 or the provider's @2x,
and draws each icon at LabelIconRef size times its own device ratio. A host with a GPU texture
ceiling sets RentileConfiguration.labelGlyphAtlas to
LabelGlyphAtlasPolicy(LabelGlyphPacking.REFERENCED_GLYPHS, maxDimensionPx = 4096), so the glyph
atlas holds only the glyphs its candidates draw and never exceeds that width or height.
See ADR 0024,
ADR 0036 and the
0.6.0 migration guide.
That is the default profile, CompatibilityPolicy.RentileV1, which still draws icons that do not
depend on text into the Output Tile. A host that draws every symbol itself prepares the style with
CompatibilityPolicy.RentileV1HostSymbols instead: its Output Tiles carry no symbol layer at all,
every visible vector symbol layer with text or an icon becomes a label layer, an icon with no
text - or whose text is lost - becomes an icon-only candidate, and icon-only candidates survive a
style without glyphs. Each candidate also carries its feature id and evaluated text, so a host can
de-duplicate a feature anchored in several tiles, and its text-size and icon-size as functions
of the camera's fractional zoom that reproduce Mapbox GL exactly. A LabelCandidateOptions
textFieldOverride applies one text-field to every label layer at acquisition - a label-language
setting - without changing the prepared style or any Output Tile key. See
ADR 0035 and the
0.12.0 migration guide.
acquireTerrainTiles returns the exact encoded DEM bytes and the pixels the decode that
validated them produced. ValidatedDemTile.texels is canonical 8-bit RGBA - fixed red, green, blue,
alpha byte order, rows top-down and tightly packed at width * 4 bytes, never premultiplied and
never colour-converted - so a consumer reads elevation without owning a decoder for whichever
container the provider serves, which in a real style corpus is usually WebP rather than PNG.
Those are packed channel values, not metres: apply the tile's own encoding to them. bytes
remains the exact resource and remains the right value to hash for cache identity. Nothing but
terrain acquisition retains decoded pixels, and nothing is decoded twice.
See ADR 0029 and the
0.7.0 migration guide.
Release versions must not be hardcoded in HTML. Every displayed Rentile release uses
data-maven-version="kmp", and every applicable page loads the shared docs/versions.js script.
The browser reads <versioning><release> directly from
https://maven.rohittp.com/com/rohittp/rentile/kmp/maven-metadata.xml; it keeps the readable
latest fallback if metadata cannot be loaded. The R2 CORS origin is https://rohittp.com.
Publishing a new release requires no documentation commit or version-sync automation.
Rentile raises typed RentileException subclasses with stable error codes, pipeline stages, redacted diagnostics, and affected tile identities. It does not retry, fall back, or return a partial output batch. CancellationException is propagated unchanged so callers retain control of cancellation. Callers also mark each render URGENT or NORMAL through RenderPriority, which decides only which waiting request the next freed metatile worker serves. Raw-resource cache entries completed for other tiles are not rolled back when a later tile fails.
Messages and causes from injected transport/store adapters are not forwarded because they may contain signed URLs or secret-bearing paths. Record adapter-specific failures in redacted form inside the adapter, and use Rentile's typed status, retry delay, resource class, stage, and affected tiles for recovery decisions.
Rentile is licensed under Apache-2.0. See THIRD_PARTY_NOTICES.md for dependency and adapted-code notices.
Rentile is a headless Kotlin Multiplatform basemap tile rasterizer. It accepts a supported map style and north-up XYZ tile identities, performs bounded local CPU rendering, and returns encoded PNG bytes without a UI view or platform render loop.
Rentile is published to the public repository at https://maven.rohittp.com. Every push to main that changes anything outside documentation publishes a new release, taking the highest version already public and advancing its patch component. Set VERSION_NAME in the root gradle.properties above every published version to cut a deliberate minor or major release instead. Releases cannot overwrite an existing coordinate.
0.15.1 upgrades Skiko and the bundled Android native libraries to 0.150.2. Consumers that
depend on Skiko directly must align with Rentile's strict version constraint. See the
0.15.1 migration guide.
0.15.0 adds opt-in temporary coverage without hillshade and reduces CPU hillshade sampling
work. Full-detail output remains the default, with unchanged cache keys. The appended
RenderOptions field changes constructor/copy ABI, so hosts must recompile. See the
0.15.0 migration guide.
0.13.0 added host scenery support. It adds opt-in host-owned extrusion
polygons and style evaluation, with selective decoding and per-operation memory limits. Existing
profiles retain their raster behavior. See the 0.13.0 migration guide
and ADR 0037. Publication is complete
only when the release workflow passes and all eight public coordinates resolve.
0.12.1 fixes line and line-center label text: it is laid out on one line, as Mapbox lays it
out, instead of wrapping at text-max-width like point text. No public signatures changed; the two
label-candidate keys moved. See the 0.12.1 migration guide.
0.12.0 is a deliberate breaking minor. It lets a host own every symbol layer, hands it the style's
sprite sheet, and bounds the label glyph atlas; RentileConfiguration, LabelCandidate and
LabelIconRef gain appended fields, so every consumer recompiles, and the two label-candidate keys
move. See the 0.12.0 migration guide.
0.11.4 is whole on all eight coordinates and a drop-in for 0.11.3: a filter on a raster,
hillshade or background layer is ignored instead of failing the style. See the
0.11.4 ledger and, for 0.11.3, the
0.11.3 migration guide. Consumers upgrading from 0.11.0 or
0.11.1 should also read the 0.11.2 guide, which carries the one change
an OkHttp-backed transport has to make for Rentile's per-origin budget to be spendable.
0.11.0 and 0.11.1 are both published without a kmp-macosarm64 coordinate, so a consumer with
that target resolves a 404 from either. A released coordinate is immutable; move to 0.11.2 or
later. See the 0.11.1 migration ledger.
arm64-v8a and x86_64
jvm
iosArm64, iosSimulatorArm64, and macosArm64
linuxX64 and linuxArm64
Apple support is Apple Silicon only; Rentile does not publish iosX64 or macosX64. See ADR 0022.
Rentile publishes one consumer coordinate:
commonMain.dependencies {
implementation("com.rohittp.rentile:kmp:<version>")
}Add the shared public repository before the standard repositories. No repository credentials are required. Rentile is an ordinary KMP dependency, so it resolves through dependencyResolutionManagement; this project does not publish a Gradle plugin. Skiko's platform artifacts still come from JetBrains' public Compose repository.
dependencyResolutionManagement {
repositories {
maven("https://maven.rohittp.com")
mavenCentral()
google()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") {
content { includeGroup("org.jetbrains.skiko") }
}
}
}python3 tools/check_coverage_manifest.py compatibility/rentile-v1-coverage.json
python3 tools/check_corpus_fidelity_policy.py compatibility/rentile-v1-coverage.json
./gradlew :kmp:checkKotlinAbi
./gradlew :kmp:testAndroidHostTest :kmp:jvmTest :kmp:macosArm64Test
./gradlew :kmp:compileKotlinIosArm64 :kmp:compileKotlinIosSimulatorArm64
./gradlew :kmp:compileKotlinLinuxX64 :kmp:compileKotlinLinuxArm64
./gradlew :kmp:publishAllPublicationsToLocalTestRepository
./gradlew -p consumer-smoke compileAndroidMain compileKotlinJvm compileKotlinIosArm64 compileKotlinIosSimulatorArm64 compileKotlinMacosArm64 compileKotlinLinuxX64 compileKotlinLinuxArm64
Merging a non-documentation change to main starts one publication automatically. Do not also
dispatch the workflow manually for the same commit: a second run can legitimately resolve the next
patch version. Use manual dispatch only when no push-triggered run exists. To dispatch deliberately,
or to watch the run a push started:
gh workflow run publish.yml --repo rohittp0/rentile --ref main
gh run list --repo rohittp0/rentile --workflow publish.yml --limit 1
gh run watch RUN_ID --repo rohittp0/rentile --exit-status
Architecture decisions and the evolving contract are in docs/. Public documentation is prepared for https://rohittp.com/rentile/.
The rolling corpus is discovered from the public paginated map catalog and checked against a credential-free Coverage Manifest. See compatibility/README.md for local and workflow usage.
Rentile does not bake text into Output Tiles. Under the default profile it admits every visible
text-bearing vector symbol layer to descriptor and candidate compilation (the host-owned-symbols
profile below admits icon-only layers too), including point, line, and line-center labels, and
carries each successfully resolved paired icon so the viewport-owning consumer can place and
collide the complete symbol. Unsupported scripts, constructs, sources, and feature values are
excluded with stable diagnostics instead of being represented as plausible but incorrect labels.
The public API exposes
glyph geometry, source-line geometry, feature-resolved paint, explicit overlap, alignment, and
z-order enums, and icon text-fit inputs. LabelIconRef now carries a LabelIconAnchor for the final
fitted box instead of the removed pre-fit anchorOffsetX/Y shifts. The consumer resolves sprite
imagery by LabelIconRef.imageName and still owns projection, shaping limitations, cross-tile
collision, depth, and final drawing. The label batch carries no sprite sheet; a consumer that draws
icons itself calls acquireSpriteAtlas for the style's sheet, at ratio 1 or the provider's @2x,
and draws each icon at LabelIconRef size times its own device ratio. A host with a GPU texture
ceiling sets RentileConfiguration.labelGlyphAtlas to
LabelGlyphAtlasPolicy(LabelGlyphPacking.REFERENCED_GLYPHS, maxDimensionPx = 4096), so the glyph
atlas holds only the glyphs its candidates draw and never exceeds that width or height.
See ADR 0024,
ADR 0036 and the
0.6.0 migration guide.
That is the default profile, CompatibilityPolicy.RentileV1, which still draws icons that do not
depend on text into the Output Tile. A host that draws every symbol itself prepares the style with
CompatibilityPolicy.RentileV1HostSymbols instead: its Output Tiles carry no symbol layer at all,
every visible vector symbol layer with text or an icon becomes a label layer, an icon with no
text - or whose text is lost - becomes an icon-only candidate, and icon-only candidates survive a
style without glyphs. Each candidate also carries its feature id and evaluated text, so a host can
de-duplicate a feature anchored in several tiles, and its text-size and icon-size as functions
of the camera's fractional zoom that reproduce Mapbox GL exactly. A LabelCandidateOptions
textFieldOverride applies one text-field to every label layer at acquisition - a label-language
setting - without changing the prepared style or any Output Tile key. See
ADR 0035 and the
0.12.0 migration guide.
acquireTerrainTiles returns the exact encoded DEM bytes and the pixels the decode that
validated them produced. ValidatedDemTile.texels is canonical 8-bit RGBA - fixed red, green, blue,
alpha byte order, rows top-down and tightly packed at width * 4 bytes, never premultiplied and
never colour-converted - so a consumer reads elevation without owning a decoder for whichever
container the provider serves, which in a real style corpus is usually WebP rather than PNG.
Those are packed channel values, not metres: apply the tile's own encoding to them. bytes
remains the exact resource and remains the right value to hash for cache identity. Nothing but
terrain acquisition retains decoded pixels, and nothing is decoded twice.
See ADR 0029 and the
0.7.0 migration guide.
Release versions must not be hardcoded in HTML. Every displayed Rentile release uses
data-maven-version="kmp", and every applicable page loads the shared docs/versions.js script.
The browser reads <versioning><release> directly from
https://maven.rohittp.com/com/rohittp/rentile/kmp/maven-metadata.xml; it keeps the readable
latest fallback if metadata cannot be loaded. The R2 CORS origin is https://rohittp.com.
Publishing a new release requires no documentation commit or version-sync automation.
Rentile raises typed RentileException subclasses with stable error codes, pipeline stages, redacted diagnostics, and affected tile identities. It does not retry, fall back, or return a partial output batch. CancellationException is propagated unchanged so callers retain control of cancellation. Callers also mark each render URGENT or NORMAL through RenderPriority, which decides only which waiting request the next freed metatile worker serves. Raw-resource cache entries completed for other tiles are not rolled back when a later tile fails.
Messages and causes from injected transport/store adapters are not forwarded because they may contain signed URLs or secret-bearing paths. Record adapter-specific failures in redacted form inside the adapter, and use Rentile's typed status, retry delay, resource class, stage, and affected tiles for recovery decisions.
Rentile is licensed under Apache-2.0. See THIRD_PARTY_NOTICES.md for dependency and adapted-code notices.