mirror of
https://github.com/PolyhedralDev/Terra.git
synced 2026-04-12 18:56:04 +00:00
Image library initial implementation
*Moves the BufferedImage loader into the library, rather than being a loader implemented in common/implementation
This commit is contained in:
8
common/addons/library-image/build.gradle.kts
Normal file
8
common/addons/library-image/build.gradle.kts
Normal file
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.dfsek.terra.addons.image;
|
||||
|
||||
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.dfsek.terra.addons.image.config.BufferedImageLoader;
|
||||
import com.dfsek.terra.addons.image.config.picker.SingleColorPickerTemplate;
|
||||
import com.dfsek.terra.addons.image.config.picker.TileColorPickerTemplate;
|
||||
import com.dfsek.terra.addons.image.picker.ColorPicker;
|
||||
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;
|
||||
|
||||
|
||||
public class ImageLibraryAddon implements AddonInitializer {
|
||||
|
||||
public static final TypeKey<Supplier<ObjectTemplate<ColorPicker>>> COLOR_PICKER_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(10)
|
||||
.then(event -> {
|
||||
event.getPack().applyLoader(BufferedImage.class, new BufferedImageLoader(event.getPack().getLoader()));
|
||||
})
|
||||
.then(event -> {
|
||||
CheckedRegistry<Supplier<ObjectTemplate<ColorPicker>>> colorPickerRegistry = event.getPack().getOrCreateRegistry(
|
||||
COLOR_PICKER_REGISTRY_KEY);
|
||||
colorPickerRegistry.register(addon.key("SINGLE"), SingleColorPickerTemplate::new);
|
||||
colorPickerRegistry.register(addon.key("TILED"), TileColorPickerTemplate::new);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,4 @@
|
||||
/*
|
||||
* This file is part of Terra.
|
||||
*
|
||||
* Terra is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Terra is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Terra. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.dfsek.terra.config.loaders.config;
|
||||
package com.dfsek.terra.addons.image.config;
|
||||
|
||||
import com.dfsek.tectonic.api.depth.DepthTracker;
|
||||
import com.dfsek.tectonic.api.exception.LoadException;
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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>> {
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
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();
|
||||
|
||||
@Override
|
||||
public ColorConverter<T> get() {
|
||||
return new ExactColorConverter<T>(getMapping().get(), getFallback());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.dfsek.terra.addons.image.config.picker;
|
||||
|
||||
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.picker.ColorPicker;
|
||||
import com.dfsek.terra.addons.image.picker.transform.Alignment;
|
||||
|
||||
|
||||
public abstract class ColorPickerTemplate implements ObjectTemplate<ColorPicker> {
|
||||
|
||||
@Value("align")
|
||||
@Default
|
||||
protected Alignment alignment = Alignment.NONE;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.dfsek.terra.addons.image.config.picker;
|
||||
|
||||
import com.dfsek.tectonic.api.config.template.annotations.Value;
|
||||
|
||||
import com.dfsek.terra.addons.image.picker.ColorPicker;
|
||||
import com.dfsek.terra.addons.image.picker.SimpleColorPicker;
|
||||
|
||||
|
||||
public class SingleColorPickerTemplate extends ColorPickerTemplate {
|
||||
|
||||
@Value("fallback")
|
||||
private int fallback;
|
||||
|
||||
@Override
|
||||
public ColorPicker get() {
|
||||
return new SimpleColorPicker(fallback, alignment);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.dfsek.terra.addons.image.config.picker;
|
||||
|
||||
import com.dfsek.terra.addons.image.picker.ColorPicker;
|
||||
import com.dfsek.terra.addons.image.picker.TileColorPicker;
|
||||
|
||||
|
||||
public class TileColorPickerTemplate extends ColorPickerTemplate {
|
||||
|
||||
@Override
|
||||
public ColorPicker get() {
|
||||
return new TileColorPicker(alignment);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
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(Integer color) {
|
||||
int closest = 0;
|
||||
int smallestDistance = Integer.MAX_VALUE;
|
||||
for(int compare : colors) {
|
||||
int distance = ColorUtil.distance(color, compare);
|
||||
if(distance == 0) {
|
||||
closest = compare;
|
||||
break;
|
||||
} else if(distance < smallestDistance) {
|
||||
smallestDistance = distance;
|
||||
closest = compare;
|
||||
}
|
||||
}
|
||||
return map.get(closest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> getEntries() {
|
||||
return map.values();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.dfsek.terra.addons.image.converter;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
|
||||
public interface ColorConverter<T> extends Function<Integer, T> {
|
||||
Iterable<T> getEntries();
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.dfsek.terra.addons.image.converter;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
public class ExactColorConverter<T> implements ColorConverter<T> {
|
||||
private final Map<Integer, T> map;
|
||||
|
||||
private final T fallback;
|
||||
|
||||
public ExactColorConverter(Map<Integer, T> map, T fallback) {
|
||||
this.map = map;
|
||||
this.fallback = fallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T apply(Integer color) {
|
||||
T lookup = map.get(color);
|
||||
if(lookup != null) return lookup;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<T> getEntries() {
|
||||
Set<T> entries = new HashSet<>(map.values());
|
||||
entries.add(fallback);
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
@@ -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())));
|
||||
}
|
||||
}
|
||||
@@ -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>> {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.dfsek.terra.addons.image.picker;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ColorPicker {
|
||||
|
||||
/**
|
||||
* @param image Lookup image
|
||||
* @param x World x coordinate
|
||||
* @param z World z coordinate
|
||||
* @return Integer representing a web color
|
||||
*/
|
||||
Integer apply(BufferedImage image, int x, int z);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.dfsek.terra.addons.image.picker;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import com.dfsek.terra.addons.image.picker.transform.ImageTransformation;
|
||||
|
||||
|
||||
public class SimpleColorPicker implements ColorPicker {
|
||||
|
||||
private final Integer fallback;
|
||||
|
||||
private final ImageTransformation transformation;
|
||||
|
||||
public SimpleColorPicker(int fallbackColor, ImageTransformation transformation) {
|
||||
this.fallback = fallbackColor;
|
||||
this.transformation = transformation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer apply(BufferedImage image, int x, int z) {
|
||||
x = transformation.transformX(image, x);
|
||||
z = transformation.transformZ(image, z);
|
||||
if(x < 0 || z < 0 || x >= image.getWidth() || z >= image.getHeight()) return fallback;
|
||||
return image.getRGB(x, z);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.dfsek.terra.addons.image.picker;
|
||||
|
||||
import net.jafama.FastMath;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import com.dfsek.terra.addons.image.picker.transform.ImageTransformation;
|
||||
|
||||
|
||||
public class TileColorPicker implements ColorPicker {
|
||||
|
||||
private final ImageTransformation transformation;
|
||||
|
||||
public TileColorPicker(ImageTransformation transformation) {
|
||||
this.transformation = transformation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer apply(BufferedImage image, 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()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.dfsek.terra.addons.image.picker.transform;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
|
||||
public enum Alignment implements ImageTransformation {
|
||||
|
||||
NONE() {
|
||||
@Override
|
||||
public int transformX(BufferedImage image, int x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int transformZ(BufferedImage image, int z) {
|
||||
return z;
|
||||
}
|
||||
},
|
||||
CENTER {
|
||||
@Override
|
||||
public int transformX(BufferedImage image, int x) {
|
||||
return x + image.getWidth() / 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int transformZ(BufferedImage image, int z) {
|
||||
return z + image.getHeight() / 2;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.dfsek.terra.addons.image.picker.transform;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
|
||||
public interface ImageTransformation {
|
||||
|
||||
int transformX(BufferedImage image, int x);
|
||||
|
||||
int transformZ(BufferedImage image, int z);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.dfsek.terra.addons.image.util;
|
||||
|
||||
import net.jafama.FastMath;
|
||||
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
public static int getRed(int rgb) {
|
||||
return rgb >> 16 & 255;
|
||||
}
|
||||
|
||||
public static int getGreen(int rgb) {
|
||||
return rgb >> 8 & 255;
|
||||
}
|
||||
|
||||
public static int getBlue(int rgb) {
|
||||
return rgb >> 0 & 255;
|
||||
}
|
||||
|
||||
public static int getAlpha(int rgb) {
|
||||
return rgb >> 24 & 255;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.dfsek.terra.addons.image.util;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
public class MapUtil {
|
||||
|
||||
private MapUtil() {}
|
||||
|
||||
/**
|
||||
* Utility method for converting config keys into integers,
|
||||
* used because Tectonic does not accept non string keys.
|
||||
*/
|
||||
public static <T> Map<Integer, T> convertKeysToInt(Map<String, T> map) {
|
||||
return map.entrySet().stream()
|
||||
.collect(Collectors.toMap(
|
||||
e -> Integer.decode(e.getKey()),
|
||||
Entry::getValue
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -32,7 +32,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -82,7 +81,6 @@ import com.dfsek.terra.api.world.chunk.generation.util.provider.ChunkGeneratorPr
|
||||
import com.dfsek.terra.config.fileloaders.FolderLoader;
|
||||
import com.dfsek.terra.config.fileloaders.ZIPLoader;
|
||||
import com.dfsek.terra.config.loaders.GenericTemplateSupplierLoader;
|
||||
import com.dfsek.terra.config.loaders.config.BufferedImageLoader;
|
||||
import com.dfsek.terra.config.preprocessor.MetaListLikePreprocessor;
|
||||
import com.dfsek.terra.config.preprocessor.MetaMapPreprocessor;
|
||||
import com.dfsek.terra.config.preprocessor.MetaNumberPreprocessor;
|
||||
@@ -282,8 +280,7 @@ public class ConfigPackImpl implements ConfigPack {
|
||||
|
||||
@Override
|
||||
public void register(TypeRegistry registry) {
|
||||
registry.registerLoader(ConfigType.class, configTypeRegistry)
|
||||
.registerLoader(BufferedImage.class, new BufferedImageLoader(loader));
|
||||
registry.registerLoader(ConfigType.class, configTypeRegistry);
|
||||
registryMap.forEach(registry::registerLoader);
|
||||
shortcuts.forEach(registry::registerLoader); // overwrite with delegated shortcuts if present
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user