mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2026-05-19 16:20:46 +00:00
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020-2021 Polyhedral Development
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# biome-provider-image-v2
|
||||||
|
|
||||||
|
Implements and registers the `IMAGE` biome provider, which
|
||||||
|
utilizes various config types provided by the `library-image` addon to
|
||||||
|
distribute biomes based on images.
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
version = version("1.0.0")
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
|
||||||
|
compileOnlyApi(project(":common:addons:library-image"))
|
||||||
|
implementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
|
||||||
|
testImplementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") {
|
||||||
|
relocate("net.jafama", "com.dfsek.terra.addons.biome.image.lib.jafama")
|
||||||
|
}
|
||||||
+51
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021 Polyhedral Development
|
||||||
|
*
|
||||||
|
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
|
||||||
|
* reference the LICENSE file in this module's root directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.dfsek.terra.addons.biome.image.v2;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.converter.ColorConverter;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.api.world.biome.Biome;
|
||||||
|
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
|
||||||
|
|
||||||
|
|
||||||
|
public class ImageBiomeProvider implements BiomeProvider {
|
||||||
|
private final int resolution;
|
||||||
|
|
||||||
|
private final ColorConverter<Biome> colorConverter;
|
||||||
|
|
||||||
|
private final ColorSampler colorSampler;
|
||||||
|
|
||||||
|
public ImageBiomeProvider(ColorConverter<Biome> colorConverter, ColorSampler colorSampler, int resolution) {
|
||||||
|
this.resolution = resolution;
|
||||||
|
this.colorConverter = colorConverter;
|
||||||
|
this.colorSampler = colorSampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Biome getBiome(int x, int y, int z, long seed) {
|
||||||
|
return getBiome(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Biome getBiome(int x, int z) {
|
||||||
|
x /= resolution;
|
||||||
|
z /= resolution;
|
||||||
|
return colorConverter.apply(colorSampler.apply(x, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<Biome> getBaseBiome(int x, int z, long seed) {
|
||||||
|
return Optional.of(getBiome(x, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<Biome> getBiomes() {
|
||||||
|
return colorConverter.getEntries();
|
||||||
|
}
|
||||||
|
}
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021 Polyhedral Development
|
||||||
|
*
|
||||||
|
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
|
||||||
|
* reference the LICENSE file in this module's root directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.dfsek.terra.addons.biome.image.v2;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.biome.image.v2.config.ImageProviderTemplate;
|
||||||
|
import com.dfsek.terra.addons.biome.image.v2.config.converter.ClosestBiomeColorConverterTemplate;
|
||||||
|
import com.dfsek.terra.addons.biome.image.v2.config.converter.ExactBiomeColorConverterTemplate;
|
||||||
|
import com.dfsek.terra.addons.biome.image.v2.config.converter.mapping.DefinedBiomeColorMappingTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.converter.ColorConverter;
|
||||||
|
import com.dfsek.terra.addons.image.converter.mapping.BiomeDefinedColorMapping;
|
||||||
|
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
|
||||||
|
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
|
||||||
|
import com.dfsek.terra.api.Platform;
|
||||||
|
import com.dfsek.terra.api.addon.BaseAddon;
|
||||||
|
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
|
||||||
|
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
|
||||||
|
import com.dfsek.terra.api.inject.annotations.Inject;
|
||||||
|
import com.dfsek.terra.api.registry.CheckedRegistry;
|
||||||
|
import com.dfsek.terra.api.util.reflection.TypeKey;
|
||||||
|
import com.dfsek.terra.api.world.biome.Biome;
|
||||||
|
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
|
||||||
|
|
||||||
|
|
||||||
|
public class ImageBiomeProviderAddon implements AddonInitializer {
|
||||||
|
public static final TypeKey<Supplier<ObjectTemplate<BiomeProvider>>> PROVIDER_REGISTRY_KEY = new TypeKey<>() {
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final TypeKey<Supplier<ObjectTemplate<ColorConverter<Biome>>>> BIOME_COLOR_CONVERTER_REGISTRY_KEY = new TypeKey<>() {
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final TypeKey<Supplier<ObjectTemplate<ColorMapping<Biome>>>> BIOME_COLOR_MAPPING_REGISTRY_KEY = new TypeKey<>() {
|
||||||
|
};
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Platform platform;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private BaseAddon addon;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
platform.getEventManager()
|
||||||
|
.getHandler(FunctionalEventHandler.class)
|
||||||
|
.register(addon, ConfigPackPreLoadEvent.class)
|
||||||
|
.priority(501)
|
||||||
|
.then(event -> {
|
||||||
|
CheckedRegistry<Supplier<ObjectTemplate<BiomeProvider>>> providerRegistry = event.getPack().getOrCreateRegistry(
|
||||||
|
PROVIDER_REGISTRY_KEY);
|
||||||
|
providerRegistry.register(addon.key("IMAGE"), ImageProviderTemplate::new);
|
||||||
|
})
|
||||||
|
.then(event -> {
|
||||||
|
CheckedRegistry<Supplier<ObjectTemplate<ColorConverter<Biome>>>> biomeColorConverterRegistry = event.getPack().getOrCreateRegistry(
|
||||||
|
BIOME_COLOR_CONVERTER_REGISTRY_KEY);
|
||||||
|
biomeColorConverterRegistry.register(addon.key("EXACT"), ExactBiomeColorConverterTemplate::new);
|
||||||
|
biomeColorConverterRegistry.register(addon.key("CLOSEST"), ClosestBiomeColorConverterTemplate::new);
|
||||||
|
})
|
||||||
|
.then(event -> {
|
||||||
|
CheckedRegistry<Supplier<ObjectTemplate<ColorMapping<Biome>>>> biomeColorMappingRegistry = event.getPack().getOrCreateRegistry(
|
||||||
|
BIOME_COLOR_MAPPING_REGISTRY_KEY);
|
||||||
|
biomeColorMappingRegistry.register(addon.key("USE_BIOME_COLORS"), () -> () -> new BiomeDefinedColorMapping<>(event.getPack().getRegistry(Biome.class), b -> b));
|
||||||
|
biomeColorMappingRegistry.register(addon.key("MAP"), DefinedBiomeColorMappingTemplate::new);
|
||||||
|
})
|
||||||
|
.failThrough();
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020-2021 Polyhedral Development
|
||||||
|
*
|
||||||
|
* The Terra Core Addons are licensed under the terms of the MIT License. For more details,
|
||||||
|
* reference the LICENSE file in this module's root directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.dfsek.terra.addons.biome.image.v2.config;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Default;
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Description;
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.biome.image.v2.ImageBiomeProvider;
|
||||||
|
import com.dfsek.terra.addons.image.converter.ColorConverter;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.api.world.biome.Biome;
|
||||||
|
import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("FieldMayBeFinal")
|
||||||
|
public class ImageProviderTemplate implements ObjectTemplate<BiomeProvider> {
|
||||||
|
|
||||||
|
@Value("resolution")
|
||||||
|
@Default
|
||||||
|
@Description("Sets the resolution at which to sample the image.")
|
||||||
|
private int resolution = 1;
|
||||||
|
|
||||||
|
@Value("color-sampler")
|
||||||
|
private ColorSampler colorSampler;
|
||||||
|
|
||||||
|
@Value("color-conversion")
|
||||||
|
private ColorConverter<Biome> colorConverter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BiomeProvider get() {
|
||||||
|
return new ImageBiomeProvider(colorConverter, colorSampler, resolution);
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package com.dfsek.terra.addons.biome.image.v2.config.converter;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.config.converter.ClosestColorConverterTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
|
||||||
|
import com.dfsek.terra.api.world.biome.Biome;
|
||||||
|
|
||||||
|
|
||||||
|
public class ClosestBiomeColorConverterTemplate extends ClosestColorConverterTemplate<Biome> {
|
||||||
|
|
||||||
|
@Value("match")
|
||||||
|
private ColorMapping<Biome> match;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ColorMapping<Biome> getMapping() {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package com.dfsek.terra.addons.biome.image.v2.config.converter;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Default;
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.config.converter.ExactColorConverterTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
|
||||||
|
import com.dfsek.terra.api.world.biome.Biome;
|
||||||
|
|
||||||
|
|
||||||
|
public class ExactBiomeColorConverterTemplate extends ExactColorConverterTemplate<Biome> {
|
||||||
|
|
||||||
|
@Value("match")
|
||||||
|
private ColorMapping<Biome> match;
|
||||||
|
|
||||||
|
@Value("else")
|
||||||
|
private Biome fallback;
|
||||||
|
|
||||||
|
@Value("ignore-alpha")
|
||||||
|
@Default
|
||||||
|
private boolean ignoreAlpha = true;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ColorMapping<Biome> getMapping() {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Biome getFallback() {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean ignoreAlpha() {
|
||||||
|
return ignoreAlpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
package com.dfsek.terra.addons.biome.image.v2.config.converter.mapping;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.config.ColorLoader.ColorString;
|
||||||
|
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
|
||||||
|
import com.dfsek.terra.addons.image.util.MapUtil;
|
||||||
|
import com.dfsek.terra.api.world.biome.Biome;
|
||||||
|
|
||||||
|
|
||||||
|
public class DefinedBiomeColorMappingTemplate implements ObjectTemplate<ColorMapping<Biome>> {
|
||||||
|
|
||||||
|
@Value("map")
|
||||||
|
Map<ColorString, Biome> map;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColorMapping<Biome> get() {
|
||||||
|
var map = MapUtil.mapKeys(this.map, ColorString::getColor);
|
||||||
|
return () -> map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
schema-version: 1
|
||||||
|
contributors:
|
||||||
|
- Terra contributors
|
||||||
|
id: biome-provider-image-v2
|
||||||
|
version: @VERSION@
|
||||||
|
entrypoints:
|
||||||
|
- "com.dfsek.terra.addons.biome.image.v2.ImageBiomeProviderAddon"
|
||||||
|
website:
|
||||||
|
issues: https://github.com/PolyhedralDev/Terra/issues
|
||||||
|
source: https://github.com/PolyhedralDev/Terra
|
||||||
|
docs: https://terra.polydev.org
|
||||||
|
license: MIT License
|
||||||
|
depends:
|
||||||
|
library-image: "1.+"
|
||||||
+6
@@ -8,6 +8,8 @@
|
|||||||
package com.dfsek.terra.addons.biome.image;
|
package com.dfsek.terra.addons.biome.image;
|
||||||
|
|
||||||
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@@ -24,6 +26,9 @@ import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
|
|||||||
|
|
||||||
|
|
||||||
public class ImageBiomeProviderAddon implements AddonInitializer {
|
public class ImageBiomeProviderAddon implements AddonInitializer {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ImageBiomeProviderAddon.class);
|
||||||
|
|
||||||
public static final TypeKey<Supplier<ObjectTemplate<BiomeProvider>>> PROVIDER_REGISTRY_KEY = new TypeKey<>() {
|
public static final TypeKey<Supplier<ObjectTemplate<BiomeProvider>>> PROVIDER_REGISTRY_KEY = new TypeKey<>() {
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -45,5 +50,6 @@ public class ImageBiomeProviderAddon implements AddonInitializer {
|
|||||||
() -> new ImageProviderTemplate(event.getPack().getRegistry(Biome.class)));
|
() -> new ImageProviderTemplate(event.getPack().getRegistry(Biome.class)));
|
||||||
})
|
})
|
||||||
.failThrough();
|
.failThrough();
|
||||||
|
logger.warn("The biome-provider-image addon is deprecated and scheduled for removal in Terra 7.0. It is recommended to use the biome-provider-image-v2 addon for future pack development instead.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -92,9 +92,9 @@ public class NoiseAddon implements AddonInitializer {
|
|||||||
noiseRegistry.register(addon.key("PROBABILITY"), ProbabilityNormalizerTemplate::new);
|
noiseRegistry.register(addon.key("PROBABILITY"), ProbabilityNormalizerTemplate::new);
|
||||||
noiseRegistry.register(addon.key("SCALE"), ScaleNormalizerTemplate::new);
|
noiseRegistry.register(addon.key("SCALE"), ScaleNormalizerTemplate::new);
|
||||||
noiseRegistry.register(addon.key("POSTERIZATION"), PosterizationNormalizerTemplate::new);
|
noiseRegistry.register(addon.key("POSTERIZATION"), PosterizationNormalizerTemplate::new);
|
||||||
|
|
||||||
noiseRegistry.register(addon.key("IMAGE"), ImageSamplerTemplate::new);
|
noiseRegistry.register(addon.key("IMAGE"), ImageSamplerTemplate::new);
|
||||||
|
|
||||||
noiseRegistry.register(addon.key("DOMAIN_WARP"), DomainWarpTemplate::new);
|
noiseRegistry.register(addon.key("DOMAIN_WARP"), DomainWarpTemplate::new);
|
||||||
|
|
||||||
noiseRegistry.register(addon.key("FBM"), BrownianMotionTemplate::new);
|
noiseRegistry.register(addon.key("FBM"), BrownianMotionTemplate::new);
|
||||||
|
|||||||
+12
@@ -8,6 +8,8 @@
|
|||||||
package com.dfsek.terra.addons.noise.config.templates;
|
package com.dfsek.terra.addons.noise.config.templates;
|
||||||
|
|
||||||
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
@@ -19,6 +21,10 @@ import com.dfsek.terra.api.noise.NoiseSampler;
|
|||||||
@SuppressWarnings({ "unused", "FieldMayBeFinal" })
|
@SuppressWarnings({ "unused", "FieldMayBeFinal" })
|
||||||
public class ImageSamplerTemplate extends SamplerTemplate<ImageSampler> {
|
public class ImageSamplerTemplate extends SamplerTemplate<ImageSampler> {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(ImageSamplerTemplate.class);
|
||||||
|
|
||||||
|
private static boolean used = false;
|
||||||
|
|
||||||
@Value("image")
|
@Value("image")
|
||||||
private @Meta BufferedImage image;
|
private @Meta BufferedImage image;
|
||||||
|
|
||||||
@@ -30,6 +36,12 @@ public class ImageSamplerTemplate extends SamplerTemplate<ImageSampler> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NoiseSampler get() {
|
public NoiseSampler get() {
|
||||||
|
if(!used) {
|
||||||
|
logger.warn("The IMAGE NoiseSampler implemented by the config-noise-function addon is deprecated. " +
|
||||||
|
"It is recommended to use the IMAGE NoiseSampler implemented by the config-noise-image " +
|
||||||
|
"addon instead.");
|
||||||
|
used = true;
|
||||||
|
}
|
||||||
return new ImageSampler(image, channel, frequency);
|
return new ImageSampler(image, channel, frequency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
version = version("1.0.0")
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
|
||||||
|
|
||||||
|
implementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
|
||||||
|
testImplementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama)
|
||||||
|
}
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
package com.dfsek.terra.addons.image;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.config.ColorLoader;
|
||||||
|
import com.dfsek.terra.addons.image.config.ColorLoader.ColorString;
|
||||||
|
import com.dfsek.terra.addons.image.config.noisesampler.ChannelNoiseSamplerTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.config.noisesampler.DistanceTransformNoiseSamplerTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.config.image.ImageTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.config.image.StitchedImageTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.config.colorsampler.ConstantColorSamplerTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.config.colorsampler.image.SingleImageColorSamplerTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.config.colorsampler.image.TileImageColorSamplerTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.config.colorsampler.mutate.RotateColorSamplerTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.config.colorsampler.mutate.TranslateColorSamplerTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.image.Image;
|
||||||
|
import com.dfsek.terra.addons.image.operator.DistanceTransform;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
|
||||||
|
import com.dfsek.terra.api.Platform;
|
||||||
|
import com.dfsek.terra.api.addon.BaseAddon;
|
||||||
|
import com.dfsek.terra.api.config.ConfigPack;
|
||||||
|
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
|
||||||
|
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
|
||||||
|
import com.dfsek.terra.api.inject.annotations.Inject;
|
||||||
|
import com.dfsek.terra.api.noise.NoiseSampler;
|
||||||
|
import com.dfsek.terra.api.registry.CheckedRegistry;
|
||||||
|
import com.dfsek.terra.api.util.reflection.TypeKey;
|
||||||
|
|
||||||
|
|
||||||
|
public class ImageLibraryAddon implements AddonInitializer {
|
||||||
|
|
||||||
|
public static final TypeKey<Supplier<ObjectTemplate<Image>>> IMAGE_REGISTRY_KEY = new TypeKey<>() {
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final TypeKey<Supplier<ObjectTemplate<ColorSampler>>> COLOR_PICKER_REGISTRY_KEY = new TypeKey<>() {
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final TypeKey<Supplier<ObjectTemplate<NoiseSampler>>> NOISE_SAMPLER_TOKEN = new TypeKey<>() {
|
||||||
|
};
|
||||||
|
@Inject
|
||||||
|
private Platform platform;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private BaseAddon addon;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
platform.getEventManager()
|
||||||
|
.getHandler(FunctionalEventHandler.class)
|
||||||
|
.register(addon, ConfigPackPreLoadEvent.class)
|
||||||
|
.priority(10)
|
||||||
|
.then(event -> {
|
||||||
|
ConfigPack pack = event.getPack();
|
||||||
|
CheckedRegistry<Supplier<ObjectTemplate<Image>>> imageRegistry = pack.getOrCreateRegistry(IMAGE_REGISTRY_KEY);
|
||||||
|
imageRegistry.register(addon.key("BITMAP"), () -> new ImageTemplate(pack.getLoader(), pack));
|
||||||
|
imageRegistry.register(addon.key("STITCHED_BITMAP"), () -> new StitchedImageTemplate(pack.getLoader(), pack));
|
||||||
|
})
|
||||||
|
.then(event -> {
|
||||||
|
event.getPack()
|
||||||
|
.applyLoader(DistanceTransform.CostFunction.class,
|
||||||
|
(type, o, loader, depthTracker) -> DistanceTransform.CostFunction.valueOf((String) o))
|
||||||
|
.applyLoader(DistanceTransform.Normalization.class,
|
||||||
|
(type, o, loader, depthTracker) -> DistanceTransform.Normalization.valueOf((String) o))
|
||||||
|
.applyLoader(ColorString.class, new ColorLoader());
|
||||||
|
|
||||||
|
CheckedRegistry<Supplier<ObjectTemplate<NoiseSampler>>> noiseRegistry = event.getPack().getOrCreateRegistry(
|
||||||
|
NOISE_SAMPLER_TOKEN);
|
||||||
|
noiseRegistry.register(addon.key("DISTANCE_TRANSFORM"), DistanceTransformNoiseSamplerTemplate::new);
|
||||||
|
noiseRegistry.register(addon.key("CHANNEL"), ChannelNoiseSamplerTemplate::new);
|
||||||
|
})
|
||||||
|
.then(event -> {
|
||||||
|
CheckedRegistry<Supplier<ObjectTemplate<ColorSampler>>> colorSamplerRegistry = event.getPack().getOrCreateRegistry(
|
||||||
|
COLOR_PICKER_REGISTRY_KEY);
|
||||||
|
colorSamplerRegistry.register(addon.key("SINGLE_IMAGE"), SingleImageColorSamplerTemplate::new);
|
||||||
|
colorSamplerRegistry.register(addon.key("TILED_IMAGE"), TileImageColorSamplerTemplate::new);
|
||||||
|
colorSamplerRegistry.register(addon.key("COLOR"), ConstantColorSamplerTemplate::new);
|
||||||
|
colorSamplerRegistry.register(addon.key("ROTATE"), RotateColorSamplerTemplate::new);
|
||||||
|
colorSamplerRegistry.register(addon.key("TRANSLATE"), TranslateColorSamplerTemplate::new);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package com.dfsek.terra.addons.image.colorsampler;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ColorSampler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param x World x coordinate
|
||||||
|
* @param z World z coordinate
|
||||||
|
* @return Integer representing a web color
|
||||||
|
*/
|
||||||
|
int apply(int x, int z);
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package com.dfsek.terra.addons.image.colorsampler.image;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.image.Image;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.image.transform.ImageTransformation;
|
||||||
|
|
||||||
|
|
||||||
|
public class SingleImageColorSampler implements ColorSampler {
|
||||||
|
|
||||||
|
private final Image image;
|
||||||
|
|
||||||
|
private final ColorSampler fallback;
|
||||||
|
|
||||||
|
private final ImageTransformation transformation;
|
||||||
|
|
||||||
|
public SingleImageColorSampler(Image image, ColorSampler fallback, ImageTransformation transformation) {
|
||||||
|
this.image = image;
|
||||||
|
this.fallback = fallback;
|
||||||
|
this.transformation = transformation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int apply(int x, int z) {
|
||||||
|
var nx = transformation.transformX(image, x);
|
||||||
|
var nz = transformation.transformZ(image, z);
|
||||||
|
if(nx < 0 || nz < 0 || nx >= image.getWidth() || nz >= image.getHeight()) return fallback.apply(x, z);
|
||||||
|
return image.getRGB(nx, nz);
|
||||||
|
}
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package com.dfsek.terra.addons.image.colorsampler.image;
|
||||||
|
|
||||||
|
import net.jafama.FastMath;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.image.Image;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.image.transform.ImageTransformation;
|
||||||
|
|
||||||
|
|
||||||
|
public class TileImageColorSampler implements ColorSampler {
|
||||||
|
|
||||||
|
private final Image image;
|
||||||
|
|
||||||
|
private final ImageTransformation transformation;
|
||||||
|
|
||||||
|
public TileImageColorSampler(Image image, ImageTransformation transformation) {
|
||||||
|
this.image = image;
|
||||||
|
this.transformation = transformation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int apply(int x, int z) {
|
||||||
|
x = transformation.transformX(image, x);
|
||||||
|
z = transformation.transformZ(image, z);
|
||||||
|
return image.getRGB(FastMath.floorMod(x, image.getWidth()), FastMath.floorMod(z, image.getHeight()));
|
||||||
|
}
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package com.dfsek.terra.addons.image.colorsampler.image.transform;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.image.Image;
|
||||||
|
|
||||||
|
|
||||||
|
public enum Alignment implements ImageTransformation {
|
||||||
|
|
||||||
|
NONE() {
|
||||||
|
@Override
|
||||||
|
public int transformX(Image image, int x) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int transformZ(Image image, int z) {
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
CENTER {
|
||||||
|
@Override
|
||||||
|
public int transformX(Image image, int x) {
|
||||||
|
return x + image.getWidth() / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int transformZ(Image image, int z) {
|
||||||
|
return z + image.getHeight() / 2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
package com.dfsek.terra.addons.image.colorsampler.image.transform;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.image.Image;
|
||||||
|
|
||||||
|
|
||||||
|
public interface ImageTransformation {
|
||||||
|
|
||||||
|
int transformX(Image image, int x);
|
||||||
|
|
||||||
|
int transformZ(Image image, int z);
|
||||||
|
}
|
||||||
+61
@@ -0,0 +1,61 @@
|
|||||||
|
package com.dfsek.terra.addons.image.colorsampler.mutate;
|
||||||
|
|
||||||
|
import net.jafama.FastMath;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
|
||||||
|
public class RotateColorSampler implements ColorSampler {
|
||||||
|
|
||||||
|
private final ColorSampler sampler;
|
||||||
|
|
||||||
|
private final double radians;
|
||||||
|
|
||||||
|
private final RotationMethod rotationMethod;
|
||||||
|
|
||||||
|
public RotateColorSampler(ColorSampler sampler, double degrees) {
|
||||||
|
this.sampler = sampler;
|
||||||
|
|
||||||
|
double normalizedDegrees = degrees % 360.0;
|
||||||
|
if (normalizedDegrees < 0) normalizedDegrees += 360.0;
|
||||||
|
|
||||||
|
if (normalizedDegrees == 0.0)
|
||||||
|
rotationMethod = RotationMethod.DEG_0;
|
||||||
|
else if (normalizedDegrees == 90.0)
|
||||||
|
rotationMethod = RotationMethod.DEG_90;
|
||||||
|
else if (normalizedDegrees == 180.0)
|
||||||
|
rotationMethod = RotationMethod.DEG_180;
|
||||||
|
else if (normalizedDegrees == 270.0)
|
||||||
|
rotationMethod = RotationMethod.DEG_270;
|
||||||
|
else
|
||||||
|
rotationMethod = RotationMethod.RAD_ANY;
|
||||||
|
|
||||||
|
this.radians = FastMath.toRadians(degrees);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int apply(int x, int z) {
|
||||||
|
int rx = switch(rotationMethod) {
|
||||||
|
case DEG_0 -> x;
|
||||||
|
case DEG_90 -> -z;
|
||||||
|
case DEG_180 -> -x;
|
||||||
|
case DEG_270 -> z;
|
||||||
|
case RAD_ANY -> (int) (x * FastMath.cos(radians) - z * FastMath.sin(radians));
|
||||||
|
};
|
||||||
|
int rz = switch(rotationMethod) {
|
||||||
|
case DEG_0 -> z;
|
||||||
|
case DEG_90 -> x;
|
||||||
|
case DEG_180 -> -z;
|
||||||
|
case DEG_270 -> -x;
|
||||||
|
case RAD_ANY -> (int) (z * FastMath.cos(radians) + x * FastMath.sin(radians));
|
||||||
|
};
|
||||||
|
return sampler.apply(rx, rz);
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum RotationMethod {
|
||||||
|
DEG_0,
|
||||||
|
DEG_90,
|
||||||
|
DEG_180,
|
||||||
|
DEG_270,
|
||||||
|
RAD_ANY,
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package com.dfsek.terra.addons.image.colorsampler.mutate;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
|
||||||
|
|
||||||
|
public class TranslateColorSampler implements ColorSampler {
|
||||||
|
|
||||||
|
private final ColorSampler sampler;
|
||||||
|
private final int translateX, translateZ;
|
||||||
|
|
||||||
|
public TranslateColorSampler(ColorSampler sampler, int translateX, int translateZ) {
|
||||||
|
this.sampler = sampler;
|
||||||
|
this.translateX = translateX;
|
||||||
|
this.translateZ = translateZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int apply(int x, int z) {
|
||||||
|
return sampler.apply(x - translateX, z - translateZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
+92
@@ -0,0 +1,92 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.depth.DepthTracker;
|
||||||
|
import com.dfsek.tectonic.api.exception.LoadException;
|
||||||
|
import com.dfsek.tectonic.api.loader.ConfigLoader;
|
||||||
|
import com.dfsek.tectonic.api.loader.type.TypeLoader;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.lang.reflect.AnnotatedType;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.config.ColorLoader.ColorString;
|
||||||
|
import com.dfsek.terra.addons.image.util.ColorUtil;
|
||||||
|
|
||||||
|
|
||||||
|
public class ColorLoader implements TypeLoader<ColorString> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColorString load(@NotNull AnnotatedType annotatedType, @NotNull Object o, @NotNull ConfigLoader configLoader,
|
||||||
|
DepthTracker depthTracker) throws LoadException {
|
||||||
|
return new ColorString((String) o);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ColorString {
|
||||||
|
|
||||||
|
private final int argb;
|
||||||
|
|
||||||
|
public ColorString(String string) throws IllegalArgumentException {
|
||||||
|
this.argb = parse(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColor() {
|
||||||
|
return argb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parse(String string) throws IllegalArgumentException {
|
||||||
|
if (string.length() == 0)
|
||||||
|
throw new IllegalArgumentException("Empty string cannot be parsed as a valid color");
|
||||||
|
|
||||||
|
String[] split = string.split(",");
|
||||||
|
|
||||||
|
if (split.length == 1)
|
||||||
|
return parseHex(string);
|
||||||
|
else if (split.length == 3)
|
||||||
|
return parseChannels("255", split[0], split[1], split[2]);
|
||||||
|
else if (split.length == 4)
|
||||||
|
return parseChannels(split[0], split[1], split[2], split[3]);
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException("Invalid channels provided, required format RED,GREEN,BLUE or ALPHA,RED,GREEN,BLUE");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parseHex(String hex) throws IllegalArgumentException {
|
||||||
|
if (hex.startsWith("#"))
|
||||||
|
hex = hex.substring(1);
|
||||||
|
|
||||||
|
int alpha = 255;
|
||||||
|
int red = 0;
|
||||||
|
int green = 0;
|
||||||
|
int blue = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if(hex.length() == 8) {
|
||||||
|
alpha = Integer.parseInt(hex.substring(0, 2), 16);
|
||||||
|
hex = hex.substring(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hex.length() != 6)
|
||||||
|
throw new IllegalArgumentException("Invalid color channels, required format AARRGGBB or RRGGBB");
|
||||||
|
|
||||||
|
red = Integer.parseInt(hex.substring(0, 2), 16);
|
||||||
|
green = Integer.parseInt(hex.substring(2, 4), 16);
|
||||||
|
blue = Integer.parseInt(hex.substring(4, 6), 16);
|
||||||
|
|
||||||
|
return ColorUtil.argbValidated(alpha, red, green, blue);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException("Failed to parse hex color", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parseChannels(String alpha, String red, String green, String blue) throws IllegalArgumentException {
|
||||||
|
try {
|
||||||
|
int a = Integer.decode(alpha);
|
||||||
|
int r = Integer.decode(red);
|
||||||
|
int g = Integer.decode(green);
|
||||||
|
int b = Integer.decode(blue);
|
||||||
|
|
||||||
|
return ColorUtil.argbValidated(a, r, g, b);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw new IllegalArgumentException("Invalid channel value", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.colorsampler;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.addons.image.config.ColorLoader.ColorString;
|
||||||
|
|
||||||
|
|
||||||
|
public class ConstantColorSamplerTemplate implements ObjectTemplate<ColorSampler> {
|
||||||
|
|
||||||
|
@Value("color")
|
||||||
|
private ColorString color;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColorSampler get() {
|
||||||
|
return ((x, z) -> color.getColor());
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.colorsampler.image;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Default;
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.image.Image;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.image.transform.Alignment;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class ImageColorSamplerTemplate implements ObjectTemplate<ColorSampler> {
|
||||||
|
|
||||||
|
@Value("image")
|
||||||
|
protected Image image;
|
||||||
|
|
||||||
|
@Value("align")
|
||||||
|
@Default
|
||||||
|
protected Alignment alignment = Alignment.NONE;
|
||||||
|
|
||||||
|
}
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.colorsampler.image;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.image.SingleImageColorSampler;
|
||||||
|
|
||||||
|
|
||||||
|
public class SingleImageColorSamplerTemplate extends ImageColorSamplerTemplate {
|
||||||
|
@Value("outside-sampler")
|
||||||
|
private ColorSampler fallback;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColorSampler get() {
|
||||||
|
return new SingleImageColorSampler(image, fallback, alignment);
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.colorsampler.image;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.image.TileImageColorSampler;
|
||||||
|
|
||||||
|
|
||||||
|
public class TileImageColorSamplerTemplate extends ImageColorSamplerTemplate {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColorSampler get() {
|
||||||
|
return new TileImageColorSampler(image, alignment);
|
||||||
|
}
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.colorsampler.mutate;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class MutateColorSamplerTemplate implements ObjectTemplate<ColorSampler> {
|
||||||
|
|
||||||
|
@Value("color-sampler")
|
||||||
|
protected ColorSampler sampler;
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.colorsampler.mutate;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.mutate.RotateColorSampler;
|
||||||
|
|
||||||
|
|
||||||
|
public class RotateColorSamplerTemplate extends MutateColorSamplerTemplate {
|
||||||
|
|
||||||
|
@Value("angle")
|
||||||
|
private double degrees;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColorSampler get() {
|
||||||
|
return new RotateColorSampler(sampler, degrees);
|
||||||
|
}
|
||||||
|
}
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.colorsampler.mutate;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.mutate.TranslateColorSampler;
|
||||||
|
|
||||||
|
|
||||||
|
public class TranslateColorSamplerTemplate extends MutateColorSamplerTemplate {
|
||||||
|
|
||||||
|
@Value("x")
|
||||||
|
private int translateX;
|
||||||
|
|
||||||
|
@Value("z")
|
||||||
|
private int translateZ;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColorSampler get() {
|
||||||
|
return new TranslateColorSampler(sampler, translateX, translateZ);
|
||||||
|
}
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.converter;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.converter.ClosestMatchColorConverter;
|
||||||
|
import com.dfsek.terra.addons.image.converter.ColorConverter;
|
||||||
|
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class ClosestColorConverterTemplate<T> implements ColorConverterTemplate<T> {
|
||||||
|
|
||||||
|
protected abstract ColorMapping<T> getMapping();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColorConverter<T> get() {
|
||||||
|
return new ClosestMatchColorConverter<T>(getMapping().get());
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.converter;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.converter.ColorConverter;
|
||||||
|
|
||||||
|
|
||||||
|
public interface ColorConverterTemplate<T> extends ObjectTemplate<ColorConverter<T>> {
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.converter;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.converter.ColorConverter;
|
||||||
|
import com.dfsek.terra.addons.image.converter.ExactColorConverter;
|
||||||
|
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class ExactColorConverterTemplate<T> implements ColorConverterTemplate<T> {
|
||||||
|
|
||||||
|
protected abstract ColorMapping<T> getMapping();
|
||||||
|
|
||||||
|
protected abstract T getFallback();
|
||||||
|
|
||||||
|
protected abstract boolean ignoreAlpha();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColorConverter<T> get() {
|
||||||
|
return new ExactColorConverter<T>(getMapping().get(), getFallback(), ignoreAlpha());
|
||||||
|
}
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.image;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.image.BufferedImageWrapper;
|
||||||
|
import com.dfsek.terra.addons.image.image.Image;
|
||||||
|
import com.dfsek.terra.api.config.ConfigPack;
|
||||||
|
import com.dfsek.terra.api.config.Loader;
|
||||||
|
import com.dfsek.terra.api.properties.Properties;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Cache prevents configs from loading the same image multiple times into memory
|
||||||
|
*/
|
||||||
|
record ImageCache(ConcurrentHashMap<String, Image> map) implements Properties {
|
||||||
|
public static Image load(String path, ConfigPack pack, Loader files) throws IOException {
|
||||||
|
ImageCache cache;
|
||||||
|
if(!pack.getContext().has(ImageCache.class)) {
|
||||||
|
cache = new ImageCache(new ConcurrentHashMap<>());
|
||||||
|
pack.getContext().put(cache);
|
||||||
|
} else {
|
||||||
|
cache = pack.getContext().get(ImageCache.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cache.map.containsKey(path)) {
|
||||||
|
return cache.map.get(path);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
BufferedImageWrapper image = new BufferedImageWrapper(ImageIO.read(files.get(path)));
|
||||||
|
cache.map.put(path, image);
|
||||||
|
return image;
|
||||||
|
} catch(IllegalArgumentException e) {
|
||||||
|
throw new IllegalArgumentException("Unable to load image (image might be too large?)", e);
|
||||||
|
} catch(IOException e) {
|
||||||
|
if(e instanceof FileNotFoundException) {
|
||||||
|
// Rethrow using nicer message
|
||||||
|
throw new IOException("Unable to load image: No such file or directory: " + path, e);
|
||||||
|
}
|
||||||
|
throw new IOException("Unable to load image", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.image;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.image.Image;
|
||||||
|
import com.dfsek.terra.api.config.ConfigPack;
|
||||||
|
import com.dfsek.terra.api.config.Loader;
|
||||||
|
|
||||||
|
|
||||||
|
public class ImageTemplate implements ObjectTemplate<Image> {
|
||||||
|
|
||||||
|
@Value("path")
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
private final Loader files;
|
||||||
|
|
||||||
|
private final ConfigPack pack;
|
||||||
|
|
||||||
|
public ImageTemplate(Loader files, ConfigPack pack) {
|
||||||
|
this.files = files;
|
||||||
|
this.pack = pack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Image get() {
|
||||||
|
try {
|
||||||
|
return ImageCache.load(path, pack, files);
|
||||||
|
} catch(IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.image;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.ValidatedConfigTemplate;
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Default;
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
import com.dfsek.tectonic.api.exception.ValidationException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.image.Image;
|
||||||
|
import com.dfsek.terra.addons.image.image.StitchedImage;
|
||||||
|
import com.dfsek.terra.api.config.ConfigPack;
|
||||||
|
import com.dfsek.terra.api.config.Loader;
|
||||||
|
|
||||||
|
|
||||||
|
public class StitchedImageTemplate implements ObjectTemplate<Image>, ValidatedConfigTemplate {
|
||||||
|
|
||||||
|
@Value("path-format")
|
||||||
|
private String path;
|
||||||
|
|
||||||
|
@Value("rows")
|
||||||
|
private int rows;
|
||||||
|
|
||||||
|
@Value("columns")
|
||||||
|
private int cols;
|
||||||
|
|
||||||
|
@Value("zero-indexed")
|
||||||
|
@Default
|
||||||
|
private boolean zeroIndexed = false;
|
||||||
|
|
||||||
|
private final Loader files;
|
||||||
|
|
||||||
|
private final ConfigPack pack;
|
||||||
|
|
||||||
|
public StitchedImageTemplate(Loader files, ConfigPack pack) {
|
||||||
|
this.files = files;
|
||||||
|
this.pack = pack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Image get() {
|
||||||
|
Image[][] grid = new Image[rows][cols];
|
||||||
|
for(int i = 0; i < rows; i++) {
|
||||||
|
for(int j = 0; j < cols; j++) {
|
||||||
|
try {
|
||||||
|
grid[i][j] = ImageCache.load(getFormattedPath(i, j), pack, files);
|
||||||
|
} catch(IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new StitchedImage(grid, zeroIndexed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFormattedPath(int row, int column) {
|
||||||
|
if (!zeroIndexed) {
|
||||||
|
row++;
|
||||||
|
column++;
|
||||||
|
}
|
||||||
|
return path.replaceFirst("\\{row}", String.valueOf(row)).replaceFirst("\\{column}", String.valueOf(column));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validate() throws ValidationException {
|
||||||
|
if(!path.contains("{row}"))
|
||||||
|
throw new ValidationException("Path format does not contain sequence '{row}'");
|
||||||
|
if(!path.contains("{column}"))
|
||||||
|
throw new ValidationException("Path format does not contain sequence '{column}'");
|
||||||
|
if(rows < 1)
|
||||||
|
throw new ValidationException("Must have at least one row");
|
||||||
|
if(cols < 1)
|
||||||
|
throw new ValidationException("Must have at least one column");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.noisesampler;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Default;
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.addons.image.noisesampler.ChannelNoiseSampler;
|
||||||
|
import com.dfsek.terra.addons.image.util.ColorUtil.Channel;
|
||||||
|
import com.dfsek.terra.api.noise.NoiseSampler;
|
||||||
|
|
||||||
|
|
||||||
|
public class ChannelNoiseSamplerTemplate implements ObjectTemplate<NoiseSampler> {
|
||||||
|
|
||||||
|
@Value("color-sampler")
|
||||||
|
private ColorSampler colorSampler;
|
||||||
|
|
||||||
|
@Value("channel")
|
||||||
|
private Channel channel;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the channel should be normalized to range [-1, 1] or not
|
||||||
|
*/
|
||||||
|
@Value("normalize")
|
||||||
|
@Default
|
||||||
|
private boolean normalize = true;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Whether to multiply color channels by the alpha channel or not. If users
|
||||||
|
* are expecting pixel transparency to reduce the output value then this should
|
||||||
|
* be set to true.
|
||||||
|
*/
|
||||||
|
@Value("premultiply")
|
||||||
|
@Default
|
||||||
|
private boolean premultiply = false;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoiseSampler get() {
|
||||||
|
return new ChannelNoiseSampler(colorSampler, channel, normalize, premultiply);
|
||||||
|
}
|
||||||
|
}
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
package com.dfsek.terra.addons.image.config.noisesampler;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Default;
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.image.Image;
|
||||||
|
import com.dfsek.terra.addons.image.operator.DistanceTransform;
|
||||||
|
import com.dfsek.terra.addons.image.operator.DistanceTransform.CostFunction;
|
||||||
|
import com.dfsek.terra.addons.image.operator.DistanceTransform.Normalization;
|
||||||
|
import com.dfsek.terra.addons.image.util.ColorUtil.Channel;
|
||||||
|
import com.dfsek.terra.api.noise.NoiseSampler;
|
||||||
|
|
||||||
|
|
||||||
|
public class DistanceTransformNoiseSamplerTemplate implements ObjectTemplate<NoiseSampler> {
|
||||||
|
|
||||||
|
@Value("image")
|
||||||
|
private Image image;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The threshold value applied to the channel specified in the 'channel' parameter that splits
|
||||||
|
* the image into a binary image. This parameter is only used for cost functions that utilize
|
||||||
|
* a binary image.
|
||||||
|
*/
|
||||||
|
@Value("threshold")
|
||||||
|
@Default
|
||||||
|
private int threshold = 127;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to true, distances calculated will be clamped to stay above the largest
|
||||||
|
* distance calculated on the edges of the image. This ensures output values do not
|
||||||
|
* appear to be cut off at the image boundaries. It is recommended to leave padding
|
||||||
|
* around the image if this is in use, such that larger evaluated distances do not
|
||||||
|
* get cut out by smaller evaluated distances close to borders. Doing so will yield
|
||||||
|
* better results.
|
||||||
|
*/
|
||||||
|
@Value("clamp-to-max-edge")
|
||||||
|
@Default
|
||||||
|
private boolean clampToEdge = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The target channel to run distance calculations on.
|
||||||
|
*/
|
||||||
|
@Value("channel")
|
||||||
|
@Default
|
||||||
|
private Channel channel = Channel.GRAYSCALE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The method of image processing applied to the specified image prior to calculating
|
||||||
|
* distances.
|
||||||
|
*/
|
||||||
|
@Value("cost-function")
|
||||||
|
@Default
|
||||||
|
private CostFunction costFunction = CostFunction.Channel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inverts the resulting binary image that may be used as a cost function.
|
||||||
|
*/
|
||||||
|
@Value("invert-threshold")
|
||||||
|
@Default
|
||||||
|
private boolean invertThreshold = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* How the final distance calculation should be redistributed.
|
||||||
|
*/
|
||||||
|
@Value("normalization")
|
||||||
|
@Default
|
||||||
|
private Normalization normalization = Normalization.None;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NoiseSampler get() {
|
||||||
|
return new DistanceTransform.Noise(new DistanceTransform(image, channel, threshold, clampToEdge, costFunction, invertThreshold), normalization);
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
package com.dfsek.terra.addons.image.converter;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.util.ColorUtil;
|
||||||
|
|
||||||
|
public class ClosestMatchColorConverter<T> implements ColorConverter<T> {
|
||||||
|
|
||||||
|
private final Map<Integer, T> map;
|
||||||
|
|
||||||
|
private final Integer[] colors;
|
||||||
|
|
||||||
|
public ClosestMatchColorConverter(Map<Integer, T> map) {
|
||||||
|
this.map = map;
|
||||||
|
this.colors = map.keySet().toArray(new Integer[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T apply(int color) {
|
||||||
|
int closest = 0;
|
||||||
|
int smallestDistance = Integer.MAX_VALUE;
|
||||||
|
for(int compare : colors) {
|
||||||
|
if(color == compare) {
|
||||||
|
closest = compare;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int distance = ColorUtil.distance(color, compare);
|
||||||
|
if(distance < smallestDistance) {
|
||||||
|
smallestDistance = distance;
|
||||||
|
closest = compare;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map.get(closest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<T> getEntries() {
|
||||||
|
return map.values();
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package com.dfsek.terra.addons.image.converter;
|
||||||
|
|
||||||
|
public interface ColorConverter<T> {
|
||||||
|
|
||||||
|
T apply(int color);
|
||||||
|
|
||||||
|
Iterable<T> getEntries();
|
||||||
|
}
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
package com.dfsek.terra.addons.image.converter;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.util.ColorUtil;
|
||||||
|
import com.dfsek.terra.addons.image.util.MapUtil;
|
||||||
|
|
||||||
|
|
||||||
|
public class ExactColorConverter<T> implements ColorConverter<T> {
|
||||||
|
private final Map<Integer, T> map;
|
||||||
|
|
||||||
|
private final T fallback;
|
||||||
|
|
||||||
|
private final boolean ignoreAlpha;
|
||||||
|
|
||||||
|
public ExactColorConverter(Map<Integer, T> map, T fallback, boolean ignoreAlpha) {
|
||||||
|
if (ignoreAlpha) {
|
||||||
|
map = MapUtil.mapKeys(map, ColorUtil::zeroAlpha);
|
||||||
|
}
|
||||||
|
this.map = map;
|
||||||
|
this.fallback = fallback;
|
||||||
|
this.ignoreAlpha = ignoreAlpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T apply(int color) {
|
||||||
|
if (ignoreAlpha) {
|
||||||
|
color = ColorUtil.zeroAlpha(color);
|
||||||
|
}
|
||||||
|
T lookup = map.get(color);
|
||||||
|
return lookup != null ? lookup : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<T> getEntries() {
|
||||||
|
Set<T> entries = new HashSet<>(map.values());
|
||||||
|
entries.add(fallback);
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
package com.dfsek.terra.addons.image.converter.mapping;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import com.dfsek.terra.api.registry.Registry;
|
||||||
|
import com.dfsek.terra.api.world.biome.Biome;
|
||||||
|
|
||||||
|
|
||||||
|
public class BiomeDefinedColorMapping<T> implements ColorMapping<T> {
|
||||||
|
|
||||||
|
Registry<Biome> biomeRegistry;
|
||||||
|
|
||||||
|
Function<Biome, T> converter;
|
||||||
|
|
||||||
|
public BiomeDefinedColorMapping(Registry<Biome> biomeRegistry, Function<Biome, T> converter) {
|
||||||
|
this.biomeRegistry = biomeRegistry;
|
||||||
|
this.converter = converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<Integer, T> get() {
|
||||||
|
Map<Biome, Integer> colorMap = new HashSet<>(biomeRegistry.entries()).stream().collect(Collectors.toMap(b -> b, Biome::getColor));
|
||||||
|
Map<Integer, Biome> output = new HashMap<>();
|
||||||
|
colorMap.forEach(((biome, color) -> {
|
||||||
|
if(!output.containsKey(color)) {
|
||||||
|
output.put(color, biome);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String.format("Biome %s has same color as %s: %x", biome.getID(), output.get(color).getID(), color));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return output.entrySet().stream().collect(Collectors.toMap(Entry::getKey, e -> converter.apply(e.getValue())));
|
||||||
|
}
|
||||||
|
}
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
package com.dfsek.terra.addons.image.converter.mapping;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
|
||||||
|
public interface ColorMapping<T> extends Supplier<Map<Integer, T>> {
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
package com.dfsek.terra.addons.image.image;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
|
||||||
|
public class BufferedImageWrapper implements Image {
|
||||||
|
|
||||||
|
private final BufferedImage image;
|
||||||
|
|
||||||
|
public BufferedImageWrapper(BufferedImage image) {
|
||||||
|
this.image = image;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRGB(int x, int y) {
|
||||||
|
return image.getRGB(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWidth() {
|
||||||
|
return image.getWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeight() {
|
||||||
|
return image.getHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package com.dfsek.terra.addons.image.image;
|
||||||
|
|
||||||
|
public interface Image {
|
||||||
|
int getRGB(int x, int y);
|
||||||
|
|
||||||
|
int getWidth();
|
||||||
|
|
||||||
|
int getHeight();
|
||||||
|
}
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
package com.dfsek.terra.addons.image.image;
|
||||||
|
|
||||||
|
public class StitchedImage implements Image {
|
||||||
|
|
||||||
|
private final Image[][] images;
|
||||||
|
|
||||||
|
private final int[] rowOffsets, columnOffsets;
|
||||||
|
|
||||||
|
private final int width, height;
|
||||||
|
|
||||||
|
public StitchedImage(Image[][] images, boolean zeroIndexed) throws IllegalArgumentException {
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
int rows = images.length;
|
||||||
|
int columns = images[0].length;
|
||||||
|
this.rowOffsets = new int[rows];
|
||||||
|
this.columnOffsets = new int[columns];
|
||||||
|
for(int i = 0; i < rows; i++) {
|
||||||
|
int rowHeight = images[i][0].getHeight();
|
||||||
|
rowOffsets[i] = height;
|
||||||
|
height += rowHeight;
|
||||||
|
for(int j = 1; j < columns; j++) {
|
||||||
|
if(images[i][j].getHeight() != rowHeight)
|
||||||
|
throw new IllegalArgumentException("Image heights in row " + (i + (zeroIndexed ? 0 : 1)) + " do not match");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(int i = 0; i < columns; i++) {
|
||||||
|
int columnWidth = images[0][i].getWidth();
|
||||||
|
columnOffsets[i] = width;
|
||||||
|
width += columnWidth;
|
||||||
|
for(int j = 1; j < rows; j++) {
|
||||||
|
if(images[i][j].getWidth() != columnWidth)
|
||||||
|
throw new IllegalArgumentException("Image widths in column " + (i + (zeroIndexed ? 0 : 1)) + " do not match");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.images = images;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getColumn(int x) {
|
||||||
|
for(int i = columnOffsets.length-1; i > 0; i--) {
|
||||||
|
if(x >= columnOffsets[i])
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getRow(int y) {
|
||||||
|
for(int i = rowOffsets.length-1; i > 0; i--) {
|
||||||
|
if(y >= rowOffsets[i])
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRGB(int x, int y) {
|
||||||
|
int row = getRow(y);
|
||||||
|
int column = getColumn(x);
|
||||||
|
return images[row][column].getRGB(x-columnOffsets[column], y-rowOffsets[row]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
}
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
package com.dfsek.terra.addons.image.noisesampler;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.addons.image.util.ColorUtil;
|
||||||
|
import com.dfsek.terra.addons.image.util.ColorUtil.Channel;
|
||||||
|
import com.dfsek.terra.api.noise.NoiseSampler;
|
||||||
|
|
||||||
|
import static com.dfsek.terra.addons.image.util.MathUtil.lerp;
|
||||||
|
|
||||||
|
|
||||||
|
public class ChannelNoiseSampler implements NoiseSampler {
|
||||||
|
|
||||||
|
private final ColorSampler colorSampler;
|
||||||
|
|
||||||
|
private final Channel channel;
|
||||||
|
|
||||||
|
private final boolean normalize;
|
||||||
|
|
||||||
|
private final boolean premultiply;
|
||||||
|
|
||||||
|
public ChannelNoiseSampler(ColorSampler colorSampler, Channel channel, boolean normalize, boolean premultiply) {
|
||||||
|
this.colorSampler = colorSampler;
|
||||||
|
this.channel = channel;
|
||||||
|
this.normalize = normalize;
|
||||||
|
this.premultiply = premultiply;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double noise(long seed, double x, double y) {
|
||||||
|
int sample = colorSampler.apply((int) x, (int) y);
|
||||||
|
int premultiplied = premultiply ? ColorUtil.premultiply(sample) : sample;
|
||||||
|
double channelValue = channel.from(premultiplied);
|
||||||
|
return normalize ? lerp(channelValue, 0, -1, 255, 1) : channelValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double noise(long seed, double x, double y, double z) {
|
||||||
|
return noise(seed, x, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
+242
@@ -0,0 +1,242 @@
|
|||||||
|
package com.dfsek.terra.addons.image.operator;
|
||||||
|
|
||||||
|
import net.jafama.FastMath;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.image.image.Image;
|
||||||
|
import com.dfsek.terra.addons.image.util.ColorUtil;
|
||||||
|
import com.dfsek.terra.addons.image.util.ColorUtil.Channel;
|
||||||
|
import com.dfsek.terra.api.noise.NoiseSampler;
|
||||||
|
|
||||||
|
import static com.dfsek.terra.addons.image.util.MathUtil.lerp;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Computes a 2D distance transform of a given image and stores the result in a 2D array of distances.
|
||||||
|
* Implementation based on the algorithm described in the paper
|
||||||
|
* <a href="https://cs.brown.edu/people/pfelzens/papers/dt-final.pdf">Distance Transforms of Sampled Functions</a>
|
||||||
|
* by Pedro F. Felzenszwalb and Daniel P. Huttenlocher.
|
||||||
|
*/
|
||||||
|
public class DistanceTransform {
|
||||||
|
|
||||||
|
private final double[][] distances;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Size bounds matching the provided image.
|
||||||
|
*/
|
||||||
|
private final int width, height;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Min and max distances of the distance computation. These may change after {@link #normalize(Normalization)} calls.
|
||||||
|
*/
|
||||||
|
private double minDistance, maxDistance;
|
||||||
|
|
||||||
|
private static final double MAX_DISTANCE_CAP = 10_000_000; // Arbitrarily large value, doubtful someone would
|
||||||
|
// ever use an image large enough to exceed this.
|
||||||
|
public DistanceTransform(Image image, Channel channel, int threshold, boolean clampToMaxEdgeDistance, CostFunction costFunction, boolean invertThreshold) {
|
||||||
|
// Construct binary image based on threshold value
|
||||||
|
boolean[][] binaryImage = new boolean[image.getWidth()][image.getHeight()];
|
||||||
|
for(int x = 0; x < image.getWidth(); x++) {
|
||||||
|
for(int y = 0; y < image.getHeight(); y++) {
|
||||||
|
binaryImage[x][y] = ColorUtil.getChannel(image.getRGB(x, y), channel) > threshold ^ invertThreshold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get edges of binary image
|
||||||
|
boolean[][] binaryImageEdge = new boolean[image.getWidth()][image.getHeight()];
|
||||||
|
for(int x = 0; x < image.getWidth(); x++) {
|
||||||
|
for(int y = 0; y < image.getHeight(); y++) {
|
||||||
|
if(!binaryImage[x][y])
|
||||||
|
binaryImageEdge[x][y] = false;
|
||||||
|
else
|
||||||
|
// If cell borders any false cell
|
||||||
|
binaryImageEdge[x][y] = x > 0 && !binaryImage[x-1][y] ||
|
||||||
|
y > 0 && !binaryImage[x][y-1] ||
|
||||||
|
x < image.getWidth ()-1 && !binaryImage[x+1][y] ||
|
||||||
|
y < image.getHeight()-1 && !binaryImage[x][y+1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double[][] function = new double[image.getWidth()][image.getHeight()];
|
||||||
|
for(int x = 0; x < image.getWidth(); x++) {
|
||||||
|
for(int y = 0; y < image.getHeight(); y++) {
|
||||||
|
function[x][y] = switch (costFunction) {
|
||||||
|
case Channel -> ColorUtil.getChannel(image.getRGB(x, y), channel);
|
||||||
|
case Threshold -> binaryImage[x][y] ? MAX_DISTANCE_CAP : 0;
|
||||||
|
case ThresholdEdge, ThresholdEdgeSigned -> binaryImageEdge[x][y] ? 0 : MAX_DISTANCE_CAP;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
distances = calculateDistance2D(function);
|
||||||
|
|
||||||
|
if(costFunction == CostFunction.ThresholdEdgeSigned) {
|
||||||
|
for(int x = 0; x < image.getWidth(); x++) {
|
||||||
|
for(int y = 0; y < image.getHeight(); y++) {
|
||||||
|
distances[x][y] *= binaryImage[x][y] ? 1 : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(clampToMaxEdgeDistance) {
|
||||||
|
// Find largest value on the edge of the image
|
||||||
|
double max = Double.NEGATIVE_INFINITY;
|
||||||
|
for(int x = 0; x < image.getWidth(); x++) {
|
||||||
|
max = Math.max(max, distances[x][0]);
|
||||||
|
max = Math.max(max, distances[x][image.getHeight()-1]);
|
||||||
|
}
|
||||||
|
for(int y = 0; y < image.getHeight(); y++) {
|
||||||
|
max = Math.max(max, distances[0][y]);
|
||||||
|
max = Math.max(max, distances[image.getWidth()-1][y]);
|
||||||
|
}
|
||||||
|
// Clamp to that largest value
|
||||||
|
for(int x = 0; x < image.getWidth(); x++) {
|
||||||
|
for(int y = 0; y < image.getHeight(); y++) {
|
||||||
|
distances[x][y] = Math.max(max, distances[x][y]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.width = image.getWidth();
|
||||||
|
this.height = image.getHeight();
|
||||||
|
|
||||||
|
setOutputRange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private double[][] calculateDistance2D(double[][] f) {
|
||||||
|
double[][] d = new double[f.length][f[0].length];
|
||||||
|
// Distance pass for each column
|
||||||
|
for(int x = 0; x < f.length; x++) {
|
||||||
|
d[x] = calculateDistance1D(f[x]);
|
||||||
|
}
|
||||||
|
// Distance pass for each row
|
||||||
|
double[] row = new double[f.length];
|
||||||
|
for(int y = 0; y < f[0].length; y++) {
|
||||||
|
for(int x = 0; x < f[0].length; x++)
|
||||||
|
row[x] = d[x][y];
|
||||||
|
row = calculateDistance1D(row);
|
||||||
|
for(int x = 0; x < f[0].length; x++) {
|
||||||
|
d[x][y] = FastMath.sqrt(row[x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double[] calculateDistance1D(double[] f) {
|
||||||
|
double[] d = new double[f.length];
|
||||||
|
int[] v = new int[f.length];
|
||||||
|
double[] z = new double[f.length+1];
|
||||||
|
int k = 0;
|
||||||
|
v[0] = 0;
|
||||||
|
z[0] = Integer.MIN_VALUE;
|
||||||
|
z[1] = Integer.MAX_VALUE;
|
||||||
|
for(int q = 1; q <= f.length-1; q++) {
|
||||||
|
double s = ((f[q]+FastMath.pow2(q))-(f[v[k]]+FastMath.pow2(v[k])))/(2*q-2*v[k]);
|
||||||
|
while (s <= z[k]) {
|
||||||
|
k--;
|
||||||
|
s = ((f[q]+FastMath.pow2(q))-(f[v[k]]+FastMath.pow2(v[k])))/(2*q-2*v[k]);
|
||||||
|
}
|
||||||
|
k++;
|
||||||
|
v[k] = q;
|
||||||
|
z[k] = s;
|
||||||
|
z[k+1] = Integer.MAX_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
k = 0;
|
||||||
|
for(int q = 0; q <= f.length-1; q++) {
|
||||||
|
while(z[k+1] < q)
|
||||||
|
k++;
|
||||||
|
d[q] = FastMath.pow2(q-v[k]) + f[v[k]];
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redistributes the stored distance computation according to the provided {@link Normalization} method.
|
||||||
|
*/
|
||||||
|
private void normalize(Normalization normalization) {
|
||||||
|
for(int x = 0; x < width; x++) {
|
||||||
|
for(int y = 0; y < height; y++) {
|
||||||
|
double d = distances[x][y];
|
||||||
|
distances[x][y] = switch(normalization) {
|
||||||
|
case None -> distances[x][y];
|
||||||
|
case Linear -> lerp(d, minDistance, -1, maxDistance, 1);
|
||||||
|
case SmoothPreserveZero -> {
|
||||||
|
if(minDistance > 0 || maxDistance < 0) {
|
||||||
|
// Can't preserve zero if it is not contained in range so just lerp
|
||||||
|
yield lerp(distances[x][y], minDistance, -1, maxDistance, 1);
|
||||||
|
} else {
|
||||||
|
if(d > 0) {
|
||||||
|
yield FastMath.pow2(d/maxDistance);
|
||||||
|
} else if(d < 0) {
|
||||||
|
yield -FastMath.pow2(d/minDistance);
|
||||||
|
} else {
|
||||||
|
yield 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setOutputRange();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setOutputRange() {
|
||||||
|
double minDistance = Double.POSITIVE_INFINITY;
|
||||||
|
double maxDistance = Double.NEGATIVE_INFINITY;
|
||||||
|
for(int x = 0; x < width; x++) {
|
||||||
|
for(int y = 0; y < height; y++) {
|
||||||
|
minDistance = Math.min(minDistance, distances[x][y]);
|
||||||
|
maxDistance = Math.max(maxDistance, distances[x][y]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.minDistance = minDistance;
|
||||||
|
this.maxDistance = maxDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum CostFunction {
|
||||||
|
Channel,
|
||||||
|
Threshold,
|
||||||
|
ThresholdEdge,
|
||||||
|
ThresholdEdgeSigned,
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Normalization {
|
||||||
|
/**
|
||||||
|
* Return the raw calculated distances.
|
||||||
|
*/
|
||||||
|
None,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redistribute the output values to fit in the range [-1, 1]
|
||||||
|
*/
|
||||||
|
Linear,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redistributes smoothly to the range [-1, 1], such that areas where distance = 0 stay 0.
|
||||||
|
* This is only really applicable to signed distance calculations, and will fall back to linear
|
||||||
|
* redistribution if the input range does not contain both positive and negative values.
|
||||||
|
*/
|
||||||
|
SmoothPreserveZero,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Noise implements NoiseSampler {
|
||||||
|
|
||||||
|
private final DistanceTransform transform;
|
||||||
|
|
||||||
|
public Noise(DistanceTransform transform, Normalization normalization) {
|
||||||
|
this.transform = transform;
|
||||||
|
transform.normalize(normalization);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double noise(long seed, double x, double y) {
|
||||||
|
if(x<0 || y<0 || x>=transform.width || y>=transform.height) return transform.minDistance;
|
||||||
|
return transform.distances[FastMath.floorToInt(x)][FastMath.floorToInt(y)];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double noise(long seed, double x, double y, double z) {
|
||||||
|
return noise(seed, x, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+322
@@ -0,0 +1,322 @@
|
|||||||
|
package com.dfsek.terra.addons.image.util;
|
||||||
|
|
||||||
|
import net.jafama.FastMath;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for manipulating 8 bit ARGB colors
|
||||||
|
*/
|
||||||
|
public class ColorUtil {
|
||||||
|
|
||||||
|
private ColorUtil() {}
|
||||||
|
|
||||||
|
public static int distance(int a, int b) {
|
||||||
|
return FastMath.abs(getRed(a) - getRed(b)) +
|
||||||
|
FastMath.abs(getGreen(a) - getGreen(b)) +
|
||||||
|
FastMath.abs(getBlue(a) - getBlue(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the red channel value of a given ARGB color value.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB color value to extract the red channel value from
|
||||||
|
* @return the red channel value of the given ARGB color value, in the range 0-255
|
||||||
|
*/
|
||||||
|
public static int getRed(int argb) {
|
||||||
|
return argb >> 16 & 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the green channel value of a given ARGB color value.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB color value to extract the green channel value from
|
||||||
|
* @return the green channel value of the given ARGB color value, in the range 0-255
|
||||||
|
*/
|
||||||
|
public static int getGreen(int argb) {
|
||||||
|
return argb >> 8 & 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the blue channel value of a given ARGB color value.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB color value to extract the blue channel value from
|
||||||
|
* @return the blue channel value of the given ARGB color value, in the range 0-255
|
||||||
|
*/
|
||||||
|
public static int getBlue(int argb) {
|
||||||
|
return argb & 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the alpha channel value of a given ARGB color value.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB color value to extract the blue channel value from
|
||||||
|
* @return the alpha channel value of the given ARGB color value, in the range 0-255
|
||||||
|
*/
|
||||||
|
public static int getAlpha(int argb) {
|
||||||
|
return argb >> 24 & 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the grayscale value of a given ARGB color value.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB color value to convert to grayscale
|
||||||
|
* @return the grayscale value of the given ARGB color value, in the range 0-255
|
||||||
|
*/
|
||||||
|
public static int getGrayscale(int argb) {
|
||||||
|
return (getRed(argb) + getGreen(argb) + getBlue(argb)) / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value of the specified channel for a given ARGB color value.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB color value to extract the channel value from
|
||||||
|
* @param channel the channel to extract the value from
|
||||||
|
* @return the value of the specified channel for the given ARGB color value, in the range 0-255
|
||||||
|
*/
|
||||||
|
public static int getChannel(int argb, Channel channel) {
|
||||||
|
return channel.from(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the red channel value of a given ARGB color value to zero.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB color value to zero the red channel of
|
||||||
|
* @return the resulting ARGB color value with the red channel set to zero
|
||||||
|
*/
|
||||||
|
public static int zeroRed(int argb) {
|
||||||
|
return argb & ~0x00FF0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the green channel value of a given ARGB color value to zero.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB color value to zero the green channel of
|
||||||
|
* @return the resulting ARGB color value with the green channel set to zero
|
||||||
|
*/
|
||||||
|
public static int zeroGreen(int argb) {
|
||||||
|
return argb & ~0x0000FF00;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the blue channel value of a given ARGB color value to zero.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB color value to zero the blue channel of
|
||||||
|
* @return the resulting ARGB color value with the blue channel set to zero
|
||||||
|
*/
|
||||||
|
public static int zeroBlue(int argb) {
|
||||||
|
return argb & ~0x000000FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the alpha channel value of a given ARGB color value to zero.
|
||||||
|
* This is the same as setting the color to fully transparent.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB color value to zero the alpha channel of
|
||||||
|
* @return the resulting ARGB color value with the alpha channel set to zero
|
||||||
|
*/
|
||||||
|
public static int zeroAlpha(int argb) {
|
||||||
|
return argb & ~0xFF000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the color channels of a given ARGB color value to zero.
|
||||||
|
* This is the same as setting the color to black, while preserving the alpha.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB color value to zero the color channel of
|
||||||
|
* @return the resulting ARGB color value with the color channels set to zero
|
||||||
|
*/
|
||||||
|
public static int zeroGrayscale(int argb) {
|
||||||
|
return argb & ~0x00FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the specified channel value of a given ARGB color value to zero.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB color value to zero the specified channel of
|
||||||
|
* @param channel the channel to zero the value of
|
||||||
|
* @return the resulting ARGB color value with the specified channel value set to zero
|
||||||
|
*/
|
||||||
|
public static int zeroChannel(int argb, Channel channel) {
|
||||||
|
return channel.zero(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiply the RGB channels of a given ARGB color value by its alpha channel value.
|
||||||
|
*
|
||||||
|
* @param argb the ARGB color value to premultiply the RGB channels of
|
||||||
|
* @return the resulting premultiplied ARGB color value
|
||||||
|
*/
|
||||||
|
public static int premultiply(int argb) {
|
||||||
|
int alpha = getAlpha(argb);
|
||||||
|
int red = (getRed(argb) * alpha + 127) / 255;
|
||||||
|
int green = (getGreen(argb) * alpha + 127) / 255;
|
||||||
|
int blue = (getBlue(argb) * alpha + 127) / 255;
|
||||||
|
return argb(alpha, red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an ARGB color value with the specified values for alpha, red, green, and blue channels.
|
||||||
|
*
|
||||||
|
* @param alpha the alpha value, between 0 and 255, to set in the ARGB color value
|
||||||
|
* @param red the red value, between 0 and 255, to set in the ARGB color value
|
||||||
|
* @param green the green value, between 0 and 255, to set in the ARGB color value
|
||||||
|
* @param blue the blue value, between 0 and 255, to set in the ARGB color value
|
||||||
|
* @return the resulting ARGB color value with the specified values for alpha, red, green, and blue channels
|
||||||
|
*/
|
||||||
|
public static int argb(int alpha, int red, int green, int blue) {
|
||||||
|
return argbAlpha(alpha) | argbRed(red) | argbGreen(green) | argbBlue(blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an ARGB color value with the specified values for alpha, red, green, and blue channels,
|
||||||
|
* after validating that each channel value is in the range 0-255.
|
||||||
|
*
|
||||||
|
* @param alpha the alpha value, between 0 and 255, to set in the ARGB color value
|
||||||
|
* @param red the red value, between 0 and 255, to set in the ARGB color value
|
||||||
|
* @param green the green value, between 0 and 255, to set in the ARGB color value
|
||||||
|
* @param blue the blue value, between 0 and 255, to set in the ARGB color value
|
||||||
|
* @return the resulting ARGB color value with the specified values for alpha, red, green, and blue channels
|
||||||
|
* @throws IllegalArgumentException if any channel value is outside the range 0-255
|
||||||
|
*/
|
||||||
|
public static int argbValidated(int alpha, int red, int green, int blue) throws IllegalArgumentException {
|
||||||
|
if (alpha < 0 || alpha > 255 ||
|
||||||
|
red < 0 || red > 255 ||
|
||||||
|
green < 0 || green > 255 ||
|
||||||
|
blue < 0 || blue > 255
|
||||||
|
) throw new IllegalArgumentException("Channel values must be in range 0-255");
|
||||||
|
return argb(alpha, red, green, blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ARGB color value with the specified alpha channel value and zero
|
||||||
|
* for the red, green, and blue channels.
|
||||||
|
*
|
||||||
|
* @param alpha the alpha channel value to set in the ARGB color value
|
||||||
|
* @return the resulting ARGB color value
|
||||||
|
*/
|
||||||
|
public static int argbAlpha(int alpha) { return alpha << 24; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ARGB color value with the specified red channel value and zero
|
||||||
|
* for the alpha, green, and blue channels.
|
||||||
|
*
|
||||||
|
* @param red the red channel value to set in the ARGB color value
|
||||||
|
* @return the resulting ARGB color value
|
||||||
|
*/
|
||||||
|
public static int argbRed(int red) { return red << 16; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ARGB color value with the specified red channel value and zero
|
||||||
|
* for the alpha, red, and blue channels.
|
||||||
|
*
|
||||||
|
* @param green the green channel value to set in the ARGB color value
|
||||||
|
* @return the resulting ARGB color value
|
||||||
|
*/
|
||||||
|
public static int argbGreen(int green) { return green << 8; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the ARGB color value with the specified blue channel value and zero
|
||||||
|
* for the alpha, red, and green channels.
|
||||||
|
*
|
||||||
|
* @param blue the blue channel value to set in the ARGB color value
|
||||||
|
* @return the resulting ARGB color value
|
||||||
|
*/
|
||||||
|
public static int argbBlue(int blue) { return blue; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an ARGB color value with the specified grayscale value for all four channels.
|
||||||
|
*
|
||||||
|
* @param value the grayscale value to set in all four channels of the ARGB color value
|
||||||
|
* @return the resulting ARGB color value with the specified grayscale value for all four channels
|
||||||
|
*/
|
||||||
|
public static int argbGrayscale(int value) { return argb(value, value, value, value); }
|
||||||
|
|
||||||
|
public enum Channel {
|
||||||
|
RED {
|
||||||
|
@Override
|
||||||
|
public int from(int argb) {
|
||||||
|
return getRed(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int zero(int argb) {
|
||||||
|
return zeroRed(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int argb(int value) {
|
||||||
|
return argbRed(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GREEN {
|
||||||
|
@Override
|
||||||
|
public int from(int argb) {
|
||||||
|
return getGreen(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int zero(int argb) {
|
||||||
|
return zeroGreen(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int argb(int value) {
|
||||||
|
return argbGreen(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
BLUE {
|
||||||
|
@Override
|
||||||
|
public int from(int argb) {
|
||||||
|
return getBlue(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int zero(int argb) {
|
||||||
|
return zeroBlue(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int argb(int value) {
|
||||||
|
return argbBlue(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
GRAYSCALE {
|
||||||
|
@Override
|
||||||
|
public int from(int argb) {
|
||||||
|
return getGrayscale(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int zero(int argb) {
|
||||||
|
return zeroGrayscale(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int argb(int value) {
|
||||||
|
return argbAlpha(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ALPHA {
|
||||||
|
@Override
|
||||||
|
public int from(int argb) {
|
||||||
|
return getAlpha(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int zero(int argb) {
|
||||||
|
return zeroAlpha(argb);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int argb(int value) {
|
||||||
|
return argbAlpha(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public abstract int from(int argb);
|
||||||
|
|
||||||
|
public abstract int zero(int argb);
|
||||||
|
|
||||||
|
public abstract int argb(int value);
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package com.dfsek.terra.addons.image.util;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
public class MapUtil {
|
||||||
|
|
||||||
|
private MapUtil() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility method for applying transformations on a map's keys.
|
||||||
|
*/
|
||||||
|
public static <O, N, T> Map<N, T> mapKeys(Map<O, T> map, Function<O, N> mappingFunction) {
|
||||||
|
return map
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
e -> mappingFunction.apply(e.getKey()),
|
||||||
|
Entry::getValue
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package com.dfsek.terra.addons.image.util;
|
||||||
|
|
||||||
|
public class MathUtil {
|
||||||
|
private MathUtil() {}
|
||||||
|
|
||||||
|
public static double lerp(double x, double x1, double y1, double x2, double y2) {
|
||||||
|
return (((y1-y2)*(x-x1))/(x1-x2))+y1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
schema-version: 1
|
||||||
|
contributors:
|
||||||
|
- Terra contributors
|
||||||
|
id: library-image
|
||||||
|
version: @VERSION@
|
||||||
|
entrypoints:
|
||||||
|
- "com.dfsek.terra.addons.image.ImageLibraryAddon"
|
||||||
|
website:
|
||||||
|
issues: https://github.com/PolyhedralDev/Terra/issues
|
||||||
|
source: https://github.com/PolyhedralDev/Terra
|
||||||
|
docs: https://terra.polydev.org
|
||||||
|
license: MIT License
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020-2023 Polyhedral Development
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
version = version("1.0.0")
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
|
||||||
|
compileOnlyApi(project(":common:addons:biome-provider-pipeline-v2"))
|
||||||
|
compileOnlyApi(project(":common:addons:library-image"))
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package com.dfsek.terra.addons.biome.pipeline.image;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source;
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.addons.image.converter.ColorConverter;
|
||||||
|
|
||||||
|
|
||||||
|
public class ImageSource implements Source {
|
||||||
|
|
||||||
|
private final ColorSampler colorSampler;
|
||||||
|
|
||||||
|
private final ColorConverter<PipelineBiome> colorConverter;
|
||||||
|
|
||||||
|
public ImageSource(ColorSampler colorSampler, ColorConverter<PipelineBiome> colorConverter) {
|
||||||
|
this.colorSampler = colorSampler;
|
||||||
|
this.colorConverter = colorConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PipelineBiome get(long seed, int x, int z) {
|
||||||
|
return colorConverter.apply(colorSampler.apply(x, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterable<PipelineBiome> getBiomes() {
|
||||||
|
return colorConverter.getEntries();
|
||||||
|
}
|
||||||
|
}
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
package com.dfsek.terra.addons.biome.pipeline.image;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.image.config.ImageSourceTemplate;
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.image.config.converter.ClosestPipelineBiomeColorConverterTemplate;
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.image.config.converter.ExactPipelineBiomeColorConverterTemplate;
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.image.config.converter.mapping.DefinedPipelineBiomeColorMappingTemplate;
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.v2.BiomePipelineAddon;
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source;
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.DelegatedPipelineBiome;
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
|
||||||
|
import com.dfsek.terra.addons.image.converter.ColorConverter;
|
||||||
|
import com.dfsek.terra.addons.image.converter.mapping.BiomeDefinedColorMapping;
|
||||||
|
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
|
||||||
|
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
|
||||||
|
import com.dfsek.terra.api.Platform;
|
||||||
|
import com.dfsek.terra.api.addon.BaseAddon;
|
||||||
|
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
|
||||||
|
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
|
||||||
|
import com.dfsek.terra.api.inject.annotations.Inject;
|
||||||
|
import com.dfsek.terra.api.registry.CheckedRegistry;
|
||||||
|
import com.dfsek.terra.api.util.reflection.TypeKey;
|
||||||
|
import com.dfsek.terra.api.world.biome.Biome;
|
||||||
|
|
||||||
|
|
||||||
|
public class PipelineImageAddon implements AddonInitializer {
|
||||||
|
|
||||||
|
public static final TypeKey<Supplier<ObjectTemplate<ColorConverter<PipelineBiome>>>> PIPELINE_BIOME_COLOR_CONVERTER_REGISTRY_KEY = new TypeKey<>() {
|
||||||
|
};
|
||||||
|
|
||||||
|
public static final TypeKey<Supplier<ObjectTemplate<ColorMapping<PipelineBiome>>>> PIPELINE_BIOME_COLOR_MAPPING_REGISTRY_KEY = new TypeKey<>() {
|
||||||
|
};
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private Platform platform;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private BaseAddon addon;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize() {
|
||||||
|
|
||||||
|
platform.getEventManager()
|
||||||
|
.getHandler(FunctionalEventHandler.class)
|
||||||
|
.register(addon, ConfigPackPreLoadEvent.class)
|
||||||
|
.priority(500)
|
||||||
|
.then(event -> {
|
||||||
|
CheckedRegistry<Supplier<ObjectTemplate<ColorConverter<PipelineBiome>>>> biomeColorConverterRegistry = event.getPack().getOrCreateRegistry(
|
||||||
|
PIPELINE_BIOME_COLOR_CONVERTER_REGISTRY_KEY);
|
||||||
|
biomeColorConverterRegistry.register(addon.key("EXACT"), ExactPipelineBiomeColorConverterTemplate::new);
|
||||||
|
biomeColorConverterRegistry.register(addon.key("CLOSEST"), ClosestPipelineBiomeColorConverterTemplate::new);
|
||||||
|
})
|
||||||
|
.then(event -> {
|
||||||
|
CheckedRegistry<Supplier<ObjectTemplate<Source>>> sourceRegistry = event.getPack().getOrCreateRegistry(BiomePipelineAddon.SOURCE_REGISTRY_KEY);
|
||||||
|
sourceRegistry.register(addon.key("IMAGE"), ImageSourceTemplate::new);
|
||||||
|
})
|
||||||
|
.then(event -> {
|
||||||
|
CheckedRegistry<Supplier<ObjectTemplate<ColorMapping<PipelineBiome>>>> biomeColorMappingRegistry = event.getPack().getOrCreateRegistry(
|
||||||
|
PIPELINE_BIOME_COLOR_MAPPING_REGISTRY_KEY);
|
||||||
|
biomeColorMappingRegistry.register(addon.key("USE_BIOME_COLORS"), () -> () -> new BiomeDefinedColorMapping<>(event.getPack().getRegistry(Biome.class), DelegatedPipelineBiome::new));
|
||||||
|
biomeColorMappingRegistry.register(addon.key("MAP"), DefinedPipelineBiomeColorMappingTemplate::new);
|
||||||
|
})
|
||||||
|
.failThrough();
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package com.dfsek.terra.addons.biome.pipeline.image.config;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.image.ImageSource;
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source;
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
|
||||||
|
import com.dfsek.terra.addons.image.colorsampler.ColorSampler;
|
||||||
|
import com.dfsek.terra.addons.image.converter.ColorConverter;
|
||||||
|
|
||||||
|
|
||||||
|
public class ImageSourceTemplate implements ObjectTemplate<Source> {
|
||||||
|
|
||||||
|
@Value("color-sampler")
|
||||||
|
private ColorSampler colorSampler;
|
||||||
|
|
||||||
|
@Value("color-conversion")
|
||||||
|
private ColorConverter<PipelineBiome> colorConverter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Source get() {
|
||||||
|
return new ImageSource(colorSampler, colorConverter);
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
package com.dfsek.terra.addons.biome.pipeline.image.config.converter;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
|
||||||
|
import com.dfsek.terra.addons.image.config.converter.ClosestColorConverterTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
|
||||||
|
|
||||||
|
|
||||||
|
public class ClosestPipelineBiomeColorConverterTemplate extends ClosestColorConverterTemplate<PipelineBiome> {
|
||||||
|
|
||||||
|
@Value("match")
|
||||||
|
private ColorMapping<PipelineBiome> match;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ColorMapping<PipelineBiome> getMapping() {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package com.dfsek.terra.addons.biome.pipeline.image.config.converter;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Default;
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
|
||||||
|
import com.dfsek.terra.addons.image.config.converter.ExactColorConverterTemplate;
|
||||||
|
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
|
||||||
|
|
||||||
|
|
||||||
|
public class ExactPipelineBiomeColorConverterTemplate extends ExactColorConverterTemplate<PipelineBiome> {
|
||||||
|
|
||||||
|
@Value("match")
|
||||||
|
private ColorMapping<PipelineBiome> match;
|
||||||
|
|
||||||
|
@Value("else")
|
||||||
|
private PipelineBiome fallback;
|
||||||
|
|
||||||
|
@Value("ignore-alpha")
|
||||||
|
@Default
|
||||||
|
private boolean ignoreAlpha = true;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ColorMapping<PipelineBiome> getMapping() {
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PipelineBiome getFallback() {
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean ignoreAlpha() {
|
||||||
|
return ignoreAlpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
package com.dfsek.terra.addons.biome.pipeline.image.config.converter.mapping;
|
||||||
|
|
||||||
|
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||||
|
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
|
||||||
|
import com.dfsek.terra.addons.image.config.ColorLoader.ColorString;
|
||||||
|
import com.dfsek.terra.addons.image.converter.mapping.ColorMapping;
|
||||||
|
import com.dfsek.terra.addons.image.util.MapUtil;
|
||||||
|
|
||||||
|
|
||||||
|
public class DefinedPipelineBiomeColorMappingTemplate implements ObjectTemplate<ColorMapping<PipelineBiome>> {
|
||||||
|
|
||||||
|
@Value("map")
|
||||||
|
Map<ColorString, PipelineBiome> map;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ColorMapping<PipelineBiome> get() {
|
||||||
|
var map = MapUtil.mapKeys(this.map, ColorString::getColor);
|
||||||
|
return () -> map;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
schema-version: 1
|
||||||
|
contributors:
|
||||||
|
- Terra contributors
|
||||||
|
id: pipeline-image
|
||||||
|
version: @VERSION@
|
||||||
|
entrypoints:
|
||||||
|
- "com.dfsek.terra.addons.biome.pipeline.image.PipelineImageAddon"
|
||||||
|
website:
|
||||||
|
issues: https://github.com/PolyhedralDev/Terra/issues
|
||||||
|
source: https://github.com/PolyhedralDev/Terra
|
||||||
|
docs: https://terra.polydev.org
|
||||||
|
license: MIT License
|
||||||
|
depends:
|
||||||
|
library-image: "1.+"
|
||||||
|
biome-provider-pipeline-v2: "1.+"
|
||||||
+4
-1
@@ -33,7 +33,10 @@ import com.dfsek.terra.api.config.ConfigPack;
|
|||||||
import com.dfsek.terra.api.config.Loader;
|
import com.dfsek.terra.api.config.Loader;
|
||||||
import com.dfsek.terra.api.properties.Properties;
|
import com.dfsek.terra.api.properties.Properties;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @deprecated Use the Image and ImageLoader class provided by the library-image addon instead. This is subject to removal in v7.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public class BufferedImageLoader implements TypeLoader<BufferedImage> {
|
public class BufferedImageLoader implements TypeLoader<BufferedImage> {
|
||||||
private final Loader files;
|
private final Loader files;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user