From d30dcf69798a421a952e4210eed29c3c64d3712a Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Fri, 19 Apr 2019 17:28:08 +0200 Subject: [PATCH 01/17] Move starting point one layer up to AbstractMapRenderer --- .../rendering/AbstractMapRenderer.java | 49 ++++++++++++++++++- .../spigotmaps/rendering/ImageRenderer.java | 41 +--------------- .../spigotmaps/rendering/TextRenderer.java | 45 +---------------- 3 files changed, 50 insertions(+), 85 deletions(-) diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java index fcd98b3..55ed9ff 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java @@ -1,11 +1,13 @@ package com.github.johnnyjayjay.spigotmaps.rendering; import com.github.johnnyjayjay.spigotmaps.Checks; +import com.github.johnnyjayjay.spigotmaps.ImageTools; import org.bukkit.entity.Player; import org.bukkit.map.MapCanvas; import org.bukkit.map.MapRenderer; import org.bukkit.map.MapView; +import java.awt.*; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -25,6 +27,8 @@ */ public abstract class AbstractMapRenderer extends MapRenderer { + protected Point startingPoint; + private final Set alreadyReceived; private final boolean renderForAllPlayers, renderOnce; private final Set receivers; @@ -32,8 +36,14 @@ public abstract class AbstractMapRenderer extends MapRenderer { private boolean stop; - protected AbstractMapRenderer(Set receivers, boolean renderOnce, Predicate precondition) { + protected AbstractMapRenderer( + Point startingPoint, + Set receivers, + boolean renderOnce, + Predicate precondition + ) { super(!receivers.isEmpty()); + this.startingPoint = startingPoint; this.renderForAllPlayers = receivers.isEmpty(); this.receivers = receivers; this.renderOnce = renderOnce; @@ -92,6 +102,24 @@ public Set getReceivers() { return Collections.unmodifiableSet(receivers); } + /** + * Returns a copy of the point where the renderer begins to render text on a map. + */ + public Point getStartingPoint() { + return new Point(startingPoint); + } + + /** + * Sets the point on the map where this renderer should start rendering. + * + * @param startingPoint the point to set. + * @throws IllegalArgumentException if the given point cannot be applied to a minecraft map or is null. + */ + public void setStartingPoint(Point startingPoint) { + Checks.checkStartingPoint(startingPoint); + this.startingPoint = new Point(startingPoint); + } + /** * Returns whether this renderer only renders once for every player. * @@ -135,6 +163,7 @@ protected static abstract class Builder> { protected final Set receivers = new HashSet<>(); protected Predicate precondition = (ctx) -> true; protected boolean renderOnce = true; + protected Point startingPoint = new Point(); /** * Returns an instance of the renderer the builder is made for. @@ -151,6 +180,7 @@ protected static abstract class Builder> { */ protected final void check() { Checks.checkNotNull(precondition, "Precondition"); + Checks.checkStartingPoint(startingPoint); } /** @@ -204,5 +234,22 @@ public U renderOnce(boolean renderOnce) { this.renderOnce = renderOnce; return (U) this; } + + /** + * Sets the coordinates (via a {@link Point} determining where to begin drawing the image on the map. + *

+ * This makes a defensive copy of the {@link Point}, so changes to the argument will not have any + * effect on this instance. + *

+ * This is not required. By default, it will start drawing from the upper left corner (0, 0). + * + * @param point a non-{@code null} {@link Point} representing the coordinates, i.e. where to begin drawing. + * @return this. + * @see ImageTools#MINECRAFT_MAP_SIZE + */ + public U startingPoint(Point point) { + this.startingPoint = new Point(point); + return (U) this; + } } } diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java index dd63865..5f884dc 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java @@ -20,7 +20,6 @@ public class ImageRenderer extends AbstractMapRenderer { private BufferedImage image; - private Point startingPoint; private ImageRenderer( Set receivers, @@ -29,9 +28,8 @@ private ImageRenderer( BufferedImage image, Point startingPoint ) { - super(receivers, renderOnce, precondition); + super(startingPoint, receivers, renderOnce, precondition); this.image = image; - this.startingPoint = startingPoint; } @Override @@ -57,24 +55,6 @@ public void setImage(BufferedImage image) { this.image = ImageTools.copyOf(image); } - /** - * Returns a copy of the point where the renderer begins to render text on a map. - */ - public Point getStartingPoint() { - return new Point(startingPoint); - } - - /** - * Sets the point on the map where this renderer should start rendering. - * - * @param startingPoint the point to set. - * @throws IllegalArgumentException if the given point cannot be applied to a minecraft map or is null. - */ - public void setStartingPoint(Point startingPoint) { - Checks.checkStartingPoint(startingPoint); - this.startingPoint = new Point(startingPoint); - } - /** * Creates a new {@link ImageRenderer} that renders a specific image for the specified players * or everyone if none are specified.. @@ -114,7 +94,6 @@ public static Builder builder() { public static class Builder extends AbstractMapRenderer.Builder { private BufferedImage image = null; - private Point startingPoint = new Point(); private Builder() { } @@ -135,7 +114,6 @@ private Builder() { @Override public ImageRenderer build() { super.check(); - Checks.checkStartingPoint(startingPoint); Checks.checkNotNull(image, "Image"); return new ImageRenderer(receivers, precondition, renderOnce, image, startingPoint); } @@ -156,22 +134,5 @@ public Builder image(BufferedImage image) { this.image = ImageTools.copyOf(image); return this; } - - /** - * Sets the coordinates (via a {@link Point} determining where to begin drawing the image on the map. - *

- * This makes a defensive copy of the {@link Point}, so changes to the argument will not have any - * effect on this instance. - *

- * This is not required. By default, it will start drawing from the upper left corner (0, 0). - * - * @param point a non-{@code null} {@link Point} representing the coordinates, i.e. where to begin drawing. - * @return this. - * @see ImageTools#MINECRAFT_MAP_SIZE - */ - public Builder startingPoint(Point point) { - this.startingPoint = new Point(point); - return this; - } } } diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java index 15a60e5..22f1dad 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java @@ -22,7 +22,6 @@ public class TextRenderer extends AbstractMapRenderer { private String text; - private Point startingPoint; private MapFont font; private TextRenderer( @@ -33,9 +32,8 @@ private TextRenderer( Point startingPoint, MapFont font ) { - super(receivers, renderOnce, precondition); + super(startingPoint, receivers, renderOnce, precondition); this.text = text; - this.startingPoint = startingPoint; this.font = font; } @@ -51,13 +49,6 @@ public String getText() { return text; } - /** - * Returns a copy of the point where the renderer begins to render text on a map. - */ - public Point getStartingPoint() { - return new Point(startingPoint); - } - /** * Returns the font used to render the given text. */ @@ -75,18 +66,6 @@ public void setText(String text) { Checks.checkNotNull(text, "Text"); this.text = text; } - - /** - * Sets the point this renderer will start rendering from on the map. - * - * @param startingPoint a new Point. - * @throws IllegalArgumentException if the given point cannot be applied to a minecraft map or is null. - */ - public void setStartingPoint(Point startingPoint) { - Checks.checkStartingPoint(startingPoint); - this.startingPoint = new Point(startingPoint); - } - /** * Sets the font the rendered text should use. * @@ -146,11 +125,6 @@ private Builder() { public TextRenderer build() { super.check(); Checks.checkNotNull(font, "Font"); - Checks.checkNotNull(startingPoint, "Starting point"); - Checks.check(startingPoint.x >= 0 && startingPoint.y >= 0, "Negative coordinates are not allowed"); - Checks.check(startingPoint.x <= ImageTools.MINECRAFT_MAP_SIZE.width - && startingPoint.y <= ImageTools.MINECRAFT_MAP_SIZE.height, - "Starting point is out of minecraft map bounds"); return new TextRenderer(receivers, precondition, renderOnce, String.join("\n", lines), startingPoint, font); } @@ -179,23 +153,6 @@ public Builder addLines(String... lines) { return addLines(Arrays.asList(lines)); } - /** - * Sets the coordinates (via a {@link Point} determining where to begin writing the text on the map. - *

- * This makes a defensive copy of the {@link Point}, so changes to the argument will not have any - * effect on this instance. - *

- * This is not required. By default, it will start drawing from the upper left corner (0, 0). - * - * @param point a {@link Point} representing the coordinates, i.e. where to begin drawing. - * @return this. - * @see ImageTools#MINECRAFT_MAP_SIZE - */ - public Builder startingPoint(Point point) { - this.startingPoint = new Point(point); - return this; - } - /** * Sets the font to use for the text. *

From 1e51c4ced0b98b27cd6a56bb77e2378bd741a035 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Fri, 19 Apr 2019 18:13:04 +0200 Subject: [PATCH 02/17] Add GifRenderer, GifImage --- build.gradle | 1 + .../github/johnnyjayjay/spigotmaps/Checks.java | 15 ++++++--------- .../spigotmaps/rendering/AbstractMapRenderer.java | 3 ++- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/build.gradle b/build.gradle index 366e797..4c4a62f 100644 --- a/build.gradle +++ b/build.gradle @@ -20,6 +20,7 @@ repositories { dependencies { compileOnly "org.spigotmc:spigot-api:1.13.2-R0.1-SNAPSHOT" + compile "com.madgag:animated-gif-lib:1.4" } task sourcesJar(type: Jar, dependsOn: classes) { diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/Checks.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/Checks.java index bf74f29..6cd6257 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/Checks.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/Checks.java @@ -1,7 +1,6 @@ package com.github.johnnyjayjay.spigotmaps; import java.awt.Point; -import java.util.Collection; /** * internal class @@ -26,16 +25,14 @@ public static void assertNotNull(Object o) { throw new AssertionError("Unexpected null value"); } - public static void checkNotEmpty(Collection collection, String name) { - check(!collection.isEmpty(), name + " must not be empty"); + public static void checkStartingPoint(Point startingPoint) { + checkNotNull(startingPoint, "Starting point"); + checkBounds(startingPoint.x, 0, ImageTools.MINECRAFT_MAP_SIZE.width, "Starting point"); + checkBounds(startingPoint.y, 0, ImageTools.MINECRAFT_MAP_SIZE.height, "Starting point"); } - public static void checkStartingPoint(Point point) { - Checks.checkNotNull(point, "Starting point"); - Checks.check(point.x >= 0 && point.y >= 0, "Negative coordinates are not allowed"); - Checks.check(point.x <= ImageTools.MINECRAFT_MAP_SIZE.width - && point.y <= ImageTools.MINECRAFT_MAP_SIZE.height, - "Starting point is out of minecraft map bounds"); + public static void checkBounds(int number, int startInclusive, int endExclusive, String name) { + check(number >= startInclusive && number < endExclusive, name + " out of bounds"); } } diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java index 55ed9ff..9232727 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java @@ -176,7 +176,8 @@ protected static abstract class Builder> { public abstract T build(); /** - * Checks whether the precondition is null. Should be called when building instances of {@link AbstractMapRenderer}. + * Checks whether the precondition is null or the starting point doesn't fit the minecraft map size. + * Should be called before building instances of {@link AbstractMapRenderer}. */ protected final void check() { Checks.checkNotNull(precondition, "Precondition"); From 6d37b0a8a036cc339c7517af7497237d1e3d6f20 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Fri, 19 Apr 2019 18:13:42 +0200 Subject: [PATCH 03/17] Added GifRenderer and GifImage --- .../spigotmaps/rendering/GifImage.java | 76 ++++++++++++ .../spigotmaps/rendering/GifRenderer.java | 112 ++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java create mode 100644 src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifRenderer.java diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java new file mode 100644 index 0000000..078c6a9 --- /dev/null +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java @@ -0,0 +1,76 @@ +package com.github.johnnyjayjay.spigotmaps.rendering; + +import com.madgag.gif.fmsware.GifDecoder; + +import java.awt.image.BufferedImage; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Spliterator; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * @author Johnny_JayJay (https://www.github.com/JohnnyJayJay) + */ +public class GifImage implements Iterable { + + private final List frames; + + private GifImage(List frames) { + this.frames = Collections.unmodifiableList(frames); + } + + public static GifImage fromDecoder(GifDecoder decoder) { + return new GifImage(IntStream.range(0, decoder.getFrameCount()) + .mapToObj((i) -> Frame.create(decoder.getFrame(i), decoder.getDelay(i))) + .collect(Collectors.toList())); + } + + @Override + public Iterator iterator() { + return frames.iterator(); + } + + @Override + public void forEach(Consumer action) { + frames.forEach(action); + } + + @Override + public Spliterator spliterator() { + return frames.spliterator(); + } + + public int getFrameCount() { + return frames.size(); + } + + public Frame get(int index) { + return frames.get(index); + } + + public static class Frame { + private final BufferedImage image; + private final int msDelay; + + private Frame(BufferedImage image, int msDelay) { + this.image = image; + this.msDelay = msDelay; + } + + public static Frame create(BufferedImage image, int msDelay) { + return new Frame(image, msDelay); + } + + public int getMsDelay() { + return msDelay; + } + + public BufferedImage getImage() { + return image; + } + } + +} diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifRenderer.java new file mode 100644 index 0000000..bb2c55e --- /dev/null +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifRenderer.java @@ -0,0 +1,112 @@ +package com.github.johnnyjayjay.spigotmaps.rendering; + +import com.github.johnnyjayjay.spigotmaps.Checks; +import org.bukkit.entity.Player; + +import java.awt.Point; +import java.util.Set; +import java.util.function.Predicate; + +/** + * @author Johnny_JayJay (https://www.github.com/JohnnyJayJay) + */ +public class GifRenderer extends AbstractMapRenderer { + + public static final int REPEAT_FOREVER = -1; + + private final GifImage image; + + private int currentFrame; + private int toRepeat; + private int ticksToWait; + + private GifRenderer( + Point startingPoint, + Set receivers, + Predicate precondition, + GifImage image, + int startFrame, + int repeat + ) { + super(startingPoint, receivers, false, precondition); + this.image = image; + this.currentFrame = startFrame; + this.toRepeat = repeat; + this.ticksToWait = msToTicks(image.get(currentFrame).getMsDelay()); + } + + private int msToTicks(int millis) { + return millis / 1000 * 20; + } + + @Override + protected void render(RenderContext context) { + if (ticksToWait-- > 0) + return; + + if (currentFrame + 1 >= image.getFrameCount()) { + currentFrame = 0; + if (--toRepeat == 0) { + this.stopRendering(); + return; + } + } + + GifImage.Frame frame = image.get(currentFrame++); + context.getCanvas().drawImage(startingPoint.x, startingPoint.y, frame.getImage()); + ticksToWait = msToTicks(frame.getMsDelay()); + } + + + public int getToRepeat() { + return toRepeat; + } + + public int getCurrentFrame() { + return currentFrame; + } + + public void setFrame(int frame) { + Checks.checkBounds(frame, 0, image.getFrameCount(), "Frame index"); + this.currentFrame = frame; + } + + public static class Builder extends AbstractMapRenderer.Builder { + + private GifImage gifImage = null; + private int startFrame = 0; + private int repeat = REPEAT_FOREVER; + + @Override + public GifRenderer build() { + renderOnce = false; + super.check(); + Checks.checkNotNull(gifImage, "GIF image"); + Checks.checkBounds(startFrame, 0, gifImage.getFrameCount(), "Frame index"); + return new GifRenderer(startingPoint, receivers, precondition, gifImage, startFrame, repeat); + } + + public Builder gif(GifImage gifImage) { + this.gifImage = gifImage; + return this; + } + + public Builder startAt(int frame) { + this.startFrame = frame; + return this; + } + + public Builder repeat(int times) { + this.repeat = times; + return this; + } + + /** + * Not a supported operation, as a GifRenderer MUST render more than once. + */ + @Override + public Builder renderOnce(boolean renderOnce) { + throw new UnsupportedOperationException("renderOnce is always false for GifRenderers and thus not allowed to be set"); + } + } +} From 26c8cc32c548fc34ab38ebd1c9227a8c3624b470 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Fri, 19 Apr 2019 18:22:46 +0200 Subject: [PATCH 04/17] add todos --- src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java | 1 + .../com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java | 1 + .../github/johnnyjayjay/spigotmaps/rendering/GifRenderer.java | 1 + 3 files changed, 3 insertions(+) diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java index 194f11f..4cbb836 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java @@ -13,6 +13,7 @@ import java.net.URLConnection; /** + * TODO convenience methods for gifs * A utility class containing several methods to adjust and get images fitting the Minecraft map format. * * @author Johnny_JayJay (https://www.github.com/JohnnyJayJay) diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java index 078c6a9..f5c6c9b 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java @@ -12,6 +12,7 @@ import java.util.stream.IntStream; /** + * TODO documentation * @author Johnny_JayJay (https://www.github.com/JohnnyJayJay) */ public class GifImage implements Iterable { diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifRenderer.java index bb2c55e..83f809f 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifRenderer.java @@ -8,6 +8,7 @@ import java.util.function.Predicate; /** + * TODO documentation * @author Johnny_JayJay (https://www.github.com/JohnnyJayJay) */ public class GifRenderer extends AbstractMapRenderer { From 429bade4f19989131691fa6dee92e9793f1129b3 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Fri, 19 Apr 2019 18:48:24 +0200 Subject: [PATCH 05/17] Increment version, rename ImageTools method, add GifImage factory method --- build.gradle | 2 +- .../java/com/github/johnnyjayjay/spigotmaps/ImageTools.java | 2 +- .../github/johnnyjayjay/spigotmaps/rendering/GifImage.java | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 4c4a62f..a43b5e5 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group 'com.github.johnnyjayjay' -version '1.0' +version '1.1' sourceCompatibility = 1.8 diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java index 4cbb836..751b60d 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java @@ -89,7 +89,7 @@ public static BufferedImage resizeToMapSize(BufferedImage image) { * @return a never-null 2-dimensional array of images. The outer index represents a row, the inner * one a column in the square arrangement of parts. */ - public static BufferedImage[][] resizeIntoMapSizedParts(BufferedImage image, boolean crop) { + public static BufferedImage[][] divideIntoMapSizedParts(BufferedImage image, boolean crop) { return divideIntoParts(crop ? cropToMapDividableSquare(image) : scaleToMapDividableSquare(image)); } diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java index f5c6c9b..4cfd8b6 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java @@ -29,6 +29,10 @@ public static GifImage fromDecoder(GifDecoder decoder) { .collect(Collectors.toList())); } + public static GifImage create(List frames) { + return new GifImage(frames); + } + @Override public Iterator iterator() { return frames.iterator(); From 1088f638659052fe9079a019fdd945962713f9e3 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Fri, 19 Apr 2019 19:08:11 +0200 Subject: [PATCH 06/17] remove unnecessary defensive copies of images --- .../spigotmaps/rendering/ImageRenderer.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java index 5f884dc..602b2b7 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java @@ -38,10 +38,10 @@ protected void render(RenderContext context) { } /** - * Returns a copy of the {@link BufferedImage} used by this renderer. + * Returns the {@link BufferedImage} used by this renderer. */ public BufferedImage getImage() { - return ImageTools.copyOf(image); + return image; } /** @@ -52,7 +52,7 @@ public BufferedImage getImage() { */ public void setImage(BufferedImage image) { Checks.checkNotNull(image, "Image"); - this.image = ImageTools.copyOf(image); + this.image = image; } /** @@ -121,9 +121,6 @@ public ImageRenderer build() { /** * Sets the image that should be rendered onto the map by this renderer. *

- * This makes a defensive copy of the {@link BufferedImage}, so changes to the argument will not - * have any effect on this instance. - *

* This is a required setting. * * @param image the non-{@code null} {@link BufferedImage} to draw. @@ -131,7 +128,7 @@ public ImageRenderer build() { * @see ImageTools */ public Builder image(BufferedImage image) { - this.image = ImageTools.copyOf(image); + this.image = image; return this; } } From 4e413fa25078a128641e2894dd85d11d0bcd9a37 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Fri, 19 Apr 2019 20:04:03 +0200 Subject: [PATCH 07/17] Add methods for gifs in ImageTools equivalent to the ones for normal images --- .../johnnyjayjay/spigotmaps/ImageTools.java | 84 ++++++++++++++++--- 1 file changed, 71 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java index 751b60d..d025d9d 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java @@ -1,5 +1,6 @@ package com.github.johnnyjayjay.spigotmaps; +import com.github.johnnyjayjay.spigotmaps.rendering.GifImage; import com.github.johnnyjayjay.spigotmaps.rendering.TextRenderer; import javax.imageio.ImageIO; @@ -11,9 +12,13 @@ import java.io.InputStream; import java.net.URL; import java.net.URLConnection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; /** - * TODO convenience methods for gifs * A utility class containing several methods to adjust and get images fitting the Minecraft map format. * * @author Johnny_JayJay (https://www.github.com/JohnnyJayJay) @@ -22,10 +27,12 @@ public final class ImageTools { /** * A {@link Dimension} representing the proportions of a Minecraft map. + * Do not mutate this value as it might break this library. */ public static final Dimension MINECRAFT_MAP_SIZE = new Dimension(128, 128); - private ImageTools() {} + private ImageTools() { + } /** * Tries to read an image from a URL using an explicit user-agent. @@ -59,6 +66,19 @@ public static BufferedImage createSingleColoredImage(Color color) { return image; } + /** + * Resizes a {@link GifImage} to the size specified in {@link #MINECRAFT_MAP_SIZE}. + * + * @param gif the non-{@code null} gif to resize. + * @return a new {@link GifImage} where each frame has the according size. + */ + public static GifImage resizeToMapSize(GifImage gif) { + List newFrames = new ArrayList<>(); + for (GifImage.Frame frame : gif) + newFrames.add(GifImage.Frame.create(resizeToMapSize(frame.getImage()), frame.getMsDelay())); + return GifImage.create(newFrames); + } + /** * Resizes an image to the size specified in {@link #MINECRAFT_MAP_SIZE}. * @@ -75,31 +95,69 @@ public static BufferedImage resizeToMapSize(BufferedImage image) { } /** - * Takes an image and resizes it in such a way that the parts returned by this method can be put together + * Takes a gif image and resizes it in a way that the parts returned by this method can be put together * to form the whole image. * The result will then be a square image and the parts will all be of the size specified in * {@link #MINECRAFT_MAP_SIZE}. + *

+ * The algorithm will make a square version of the image argument first and then divide it into parts. * + * @param gif the non-{@code null} {@link GifImage} to be divided. + * @param crop true, if each frame should be cropped to a square part in the middle (i.e. the image will not be + * resized) or false, if each frame should be resized (i.e. the whole image will be visible, + * but compressed to 1:1). + * @return a never-null List containing the parts. Empty if the gif does not have any frames. + */ + public static List divideIntoMapSizedParts(GifImage gif, boolean crop) { + if (gif.getFrameCount() == 0) + return Collections.emptyList(); + + GifImage.Frame[] newFrames = new GifImage.Frame[gif.getFrameCount()]; + for (int i = 0; i < gif.getFrameCount(); i++) { + GifImage.Frame frame = gif.get(i); + newFrames[i] = GifImage.Frame.create( + crop ? cropToMapDividableSquare(frame.getImage()) : scaleToMapDividableSquare(frame.getImage()), + frame.getMsDelay() + ); + } + + int parts = newFrames[0].getImage().getWidth() / MINECRAFT_MAP_SIZE.width * 2; + GifImage.Frame[][] dividedParts = new GifImage.Frame[parts][]; + for (int i = 0; i < newFrames.length; i++) { + GifImage.Frame frame = newFrames[i]; + BufferedImage[] imageParts = divideIntoParts(frame.getImage()); + for (int j = 0; j < imageParts.length; j++) { + dividedParts[j][i] = GifImage.Frame.create(imageParts[j], frame.getMsDelay()); + } + } + return Arrays.stream(dividedParts).map(Arrays::asList).map(GifImage::create).collect(Collectors.toList()); + } + + /** + * Takes an image and resizes it in a way that the parts returned by this method can be put together + * to form the whole image. + * The result will then be a square image and the parts will all be of the size specified in + * {@link #MINECRAFT_MAP_SIZE}. + *

* The algorithm will make a square version of the image argument first and then divide it into parts. * * @param image the non-{@code null} image to be divided. - * @param crop true, if the image should be cropped to a square part in the middle (i.e. the image will not be - * resized) or false, if the image should be resized (i.e. the whole image will be visible, - * but compressed to 1:1). - * @return a never-null 2-dimensional array of images. The outer index represents a row, the inner - * one a column in the square arrangement of parts. + * @param crop true, if the image should be cropped to a square part in the middle (i.e. the image will not be + * resized) or false, if the image should be resized (i.e. the whole image will be visible, + * but compressed to 1:1). + * @return a never-null List containing the parts. */ - public static BufferedImage[][] divideIntoMapSizedParts(BufferedImage image, boolean crop) { - return divideIntoParts(crop ? cropToMapDividableSquare(image) : scaleToMapDividableSquare(image)); + public static List divideIntoMapSizedParts(BufferedImage image, boolean crop) { + return Arrays.asList(divideIntoParts(crop ? cropToMapDividableSquare(image) : scaleToMapDividableSquare(image))); } - private static BufferedImage[][] divideIntoParts(BufferedImage image) { + private static BufferedImage[] divideIntoParts(BufferedImage image) { Dimension partSize = MINECRAFT_MAP_SIZE; int linearParts = image.getWidth() / partSize.width; - BufferedImage[][] result = new BufferedImage[linearParts][linearParts]; + BufferedImage[] result = new BufferedImage[linearParts * 2]; for (int i = 0; i < linearParts; i++) { for (int j = 0; j < linearParts; j++) { - result[j][i] = (image.getSubimage(partSize.width * j, partSize.height * i, partSize.width, partSize.height)); + result[i] = image.getSubimage(partSize.width * j, partSize.height * i, partSize.width, partSize.height); } } return result; From 0b9b0e87714163dcca88030b689b49a9e4e58dc6 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Fri, 19 Apr 2019 23:01:41 +0200 Subject: [PATCH 08/17] Created util package, added documentation --- .../johnnyjayjay/spigotmaps/MapBuilder.java | 1 + .../rendering/AbstractMapRenderer.java | 6 +- .../spigotmaps/rendering/GifImage.java | 61 +++++++++++++- .../spigotmaps/rendering/GifRenderer.java | 82 +++++++++++++++++-- .../spigotmaps/rendering/ImageRenderer.java | 6 +- .../spigotmaps/rendering/RenderContext.java | 2 +- .../spigotmaps/rendering/TextRenderer.java | 5 +- .../spigotmaps/{ => util}/Checks.java | 2 +- .../spigotmaps/{ => util}/ImageTools.java | 2 +- 9 files changed, 146 insertions(+), 21 deletions(-) rename src/main/java/com/github/johnnyjayjay/spigotmaps/{ => util}/Checks.java (95%) rename src/main/java/com/github/johnnyjayjay/spigotmaps/{ => util}/ImageTools.java (99%) diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/MapBuilder.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/MapBuilder.java index 88a482d..cbf0f57 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/MapBuilder.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/MapBuilder.java @@ -3,6 +3,7 @@ import com.github.johnnyjayjay.spigotmaps.rendering.AbstractMapRenderer; import com.github.johnnyjayjay.spigotmaps.rendering.ImageRenderer; import com.github.johnnyjayjay.spigotmaps.rendering.TextRenderer; +import com.github.johnnyjayjay.spigotmaps.util.Checks; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.map.MapRenderer; diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java index 9232727..8bf3530 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java @@ -1,13 +1,13 @@ package com.github.johnnyjayjay.spigotmaps.rendering; -import com.github.johnnyjayjay.spigotmaps.Checks; -import com.github.johnnyjayjay.spigotmaps.ImageTools; +import com.github.johnnyjayjay.spigotmaps.util.Checks; +import com.github.johnnyjayjay.spigotmaps.util.ImageTools; import org.bukkit.entity.Player; import org.bukkit.map.MapCanvas; import org.bukkit.map.MapRenderer; import org.bukkit.map.MapView; -import java.awt.*; +import java.awt.Point; import java.util.Arrays; import java.util.Collection; import java.util.Collections; diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java index 4cfd8b6..3e39db3 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifImage.java @@ -1,5 +1,6 @@ package com.github.johnnyjayjay.spigotmaps.rendering; +import com.github.johnnyjayjay.spigotmaps.util.Checks; import com.madgag.gif.fmsware.GifDecoder; import java.awt.image.BufferedImage; @@ -12,7 +13,9 @@ import java.util.stream.IntStream; /** - * TODO documentation + * A class representing a .gif image, in particular animated ones. + * + * @see GifRenderer * @author Johnny_JayJay (https://www.github.com/JohnnyJayJay) */ public class GifImage implements Iterable { @@ -23,13 +26,38 @@ private GifImage(List frames) { this.frames = Collections.unmodifiableList(frames); } + /** + * Creates a new {@link GifImage} based on the data of a {@link com.madgag.gif.fmsware.GifDecoder}. + * + * @param decoder a GifDecoder that contains an already read gif image. + * @return a new, never-{@code null} GifImage. + */ public static GifImage fromDecoder(GifDecoder decoder) { return new GifImage(IntStream.range(0, decoder.getFrameCount()) .mapToObj((i) -> Frame.create(decoder.getFrame(i), decoder.getDelay(i))) .collect(Collectors.toList())); } + /** + * Creates a new {@link GifImage} based on a List of Frames. + * + * @param frames a List of {@link Frame}s to be used in this gif. + * @return a new, never-{@code null} GifImage. + * @throws IllegalArgumentException if not all frames are of the same size. + */ public static GifImage create(List frames) { + if (frames.isEmpty()) + return new GifImage(Collections.emptyList()); + + BufferedImage first = frames.get(0).getImage(); + int width = first.getWidth(); + int height = first.getHeight(); + Checks.check( + frames.stream() + .map(Frame::getImage) + .allMatch((image) -> image.getWidth() == width && image.getHeight() == height), + "The frames must all have the same size" + ); return new GifImage(frames); } @@ -48,14 +76,29 @@ public Spliterator spliterator() { return frames.spliterator(); } + /** + * Returns the amount of frames this gif holds. + */ public int getFrameCount() { return frames.size(); } + /** + * Returns the frame at the position of the specified index. + * + * @param index the index of the {@link Frame} to retrieve. + * @return a never-{@code null} {@link Frame} that gives access to its image and delay. + * @throws IndexOutOfBoundsException if the the index is out of bounds for the list of frames. + */ public Frame get(int index) { return frames.get(index); } + /** + * A class representing a single frame in an animated gif. + * + * @see GifImage + */ public static class Frame { private final BufferedImage image; private final int msDelay; @@ -65,14 +108,30 @@ private Frame(BufferedImage image, int msDelay) { this.msDelay = msDelay; } + /** + * A factory method to create instances of this class. + * + * @param image the image this frame displays. + * @param msDelay a duration in milliseconds, i.e. how long this frame should be displayed. + * @return a new, never-{@code null} instance of {@link Frame}. + * @throws IllegalArgumentException if the given duration/delay is not positive. + */ public static Frame create(BufferedImage image, int msDelay) { + Checks.checkNotNull(image, "Image"); + Checks.check(msDelay > 0, "Duration must be positive"); return new Frame(image, msDelay); } + /** + * Returns the duration in milliseconds indicating how long this particular frame should be displayed in a gif. + */ public int getMsDelay() { return msDelay; } + /** + * Returns the image of this frame. + */ public BufferedImage getImage() { return image; } diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifRenderer.java index 83f809f..500ef40 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/GifRenderer.java @@ -1,6 +1,7 @@ package com.github.johnnyjayjay.spigotmaps.rendering; -import com.github.johnnyjayjay.spigotmaps.Checks; +import com.github.johnnyjayjay.spigotmaps.util.Checks; +import com.madgag.gif.fmsware.GifDecoder; import org.bukkit.entity.Player; import java.awt.Point; @@ -8,14 +9,20 @@ import java.util.function.Predicate; /** - * TODO documentation + * An implementation of {@link AbstractMapRenderer} that is able to render animated gifs. + * + * @see Builder * @author Johnny_JayJay (https://www.github.com/JohnnyJayJay) */ public class GifRenderer extends AbstractMapRenderer { + /** + * A constant that can be used instead of magic values for {@link Builder#repeat(int)}. Value: -1. + */ public static final int REPEAT_FOREVER = -1; private final GifImage image; + private final boolean repeatForever; private int currentFrame; private int toRepeat; @@ -34,6 +41,7 @@ private GifRenderer( this.currentFrame = startFrame; this.toRepeat = repeat; this.ticksToWait = msToTicks(image.get(currentFrame).getMsDelay()); + this.repeatForever = toRepeat < 0; } private int msToTicks(int millis) { @@ -45,9 +53,9 @@ protected void render(RenderContext context) { if (ticksToWait-- > 0) return; - if (currentFrame + 1 >= image.getFrameCount()) { + if (currentFrame >= image.getFrameCount()) { currentFrame = 0; - if (--toRepeat == 0) { + if (!repeatForever && --toRepeat == 0) { this.stopRendering(); return; } @@ -58,52 +66,110 @@ protected void render(RenderContext context) { ticksToWait = msToTicks(frame.getMsDelay()); } - + /** + * Returns how often the gif will still repeat itself or {@link #REPEAT_FOREVER} if it repeats indefinitely. + */ public int getToRepeat() { - return toRepeat; + return repeatForever ? REPEAT_FOREVER : toRepeat; } + /** + * Returns the index of the {@link GifImage.Frame} this renderer is currently at. + */ public int getCurrentFrame() { return currentFrame; } + /** + * Sets the index of the {@link GifImage.Frame} this renderer will render next. + * + * @param frame the frame to set. + * @throws IllegalArgumentException if the index is out of bounds. + */ public void setFrame(int frame) { Checks.checkBounds(frame, 0, image.getFrameCount(), "Frame index"); this.currentFrame = frame; } + /** + * Creates and returns a new instance of this class' {@link Builder}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder class used to create instances of the enclosing {@link GifRenderer} class. + * + * @author Johnny_JayJay (https://github.com/johnnyjayjay) + */ public static class Builder extends AbstractMapRenderer.Builder { private GifImage gifImage = null; private int startFrame = 0; private int repeat = REPEAT_FOREVER; + private Builder() {} + + /** + * Builds a new instance of {@link ImageRenderer} based on the settings made. + * + * @return a new instance of {@link ImageRenderer}. + * @throws IllegalArgumentException if + *

    + *
  • The precondition is {@code null}
  • + *
  • The starting point is {@code null}
  • + *
  • The gif image is {@code null}
  • + *
  • The starting point's coordinates are not positive
  • + *
  • The starting point's coordinates are out of the minecraft map size bounds
  • + *
+ */ @Override public GifRenderer build() { - renderOnce = false; super.check(); Checks.checkNotNull(gifImage, "GIF image"); Checks.checkBounds(startFrame, 0, gifImage.getFrameCount(), "Frame index"); return new GifRenderer(startingPoint, receivers, precondition, gifImage, startFrame, repeat); } + /** + * Sets the {@link GifImage} this renderer should render. + * + * @param gifImage A {@link GifImage} which can be obtained using {@link GifImage#fromDecoder(GifDecoder)} for example. + * @return this. + */ public Builder gif(GifImage gifImage) { this.gifImage = gifImage; return this; } + /** + * Sets the frame this renderer should start at. + * + * @param frame the index of the frame to start at. Must be in bounds of the {@link GifImage} set. + * @return this. + */ public Builder startAt(int frame) { this.startFrame = frame; return this; } + /** + * Sets how often the animated gif should be repeated. + * + * @param times the amount of repetitions or a negative value (e.g. {@link #REPEAT_FOREVER}) if it should repeat indefinitely. + * @return this. + */ public Builder repeat(int times) { this.repeat = times; return this; } /** - * Not a supported operation, as a GifRenderer MUST render more than once. + * Not a supported operation, because every GifRenderer MUST render more than once and this + * value can therefore not be set individually. + * + * @throws UnsupportedOperationException always. */ @Override public Builder renderOnce(boolean renderOnce) { diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java index 602b2b7..e959153 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java @@ -1,7 +1,7 @@ package com.github.johnnyjayjay.spigotmaps.rendering; -import com.github.johnnyjayjay.spigotmaps.Checks; -import com.github.johnnyjayjay.spigotmaps.ImageTools; +import com.github.johnnyjayjay.spigotmaps.util.Checks; +import com.github.johnnyjayjay.spigotmaps.util.ImageTools; import org.bukkit.entity.Player; import java.awt.Color; @@ -99,7 +99,7 @@ private Builder() { } /** - * Creates a new {@link ImageRenderer}. + * Builds a new instance of {@link ImageRenderer} based on the settings made. * * @return a new instance of {@link ImageRenderer}. * @throws IllegalArgumentException if diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/RenderContext.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/RenderContext.java index 3e30fd9..d1d58a9 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/RenderContext.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/RenderContext.java @@ -1,6 +1,6 @@ package com.github.johnnyjayjay.spigotmaps.rendering; -import com.github.johnnyjayjay.spigotmaps.Checks; +import com.github.johnnyjayjay.spigotmaps.util.Checks; import org.bukkit.entity.Player; import org.bukkit.map.MapCanvas; import org.bukkit.map.MapView; diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java index 22f1dad..a9f09c8 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java @@ -1,7 +1,6 @@ package com.github.johnnyjayjay.spigotmaps.rendering; -import com.github.johnnyjayjay.spigotmaps.Checks; -import com.github.johnnyjayjay.spigotmaps.ImageTools; +import com.github.johnnyjayjay.spigotmaps.util.Checks; import org.bukkit.entity.Player; import org.bukkit.map.MapFont; import org.bukkit.map.MinecraftFont; @@ -109,7 +108,7 @@ private Builder() { } /** - * Builds a new instance of {@link TextRenderer}. + * Builds a new instance of {@link TextRenderer} based on the settings made. * * @return a never-null instance of {@link TextRenderer}. * @throws IllegalArgumentException if: diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/Checks.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/util/Checks.java similarity index 95% rename from src/main/java/com/github/johnnyjayjay/spigotmaps/Checks.java rename to src/main/java/com/github/johnnyjayjay/spigotmaps/util/Checks.java index 6cd6257..0becdbc 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/Checks.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/util/Checks.java @@ -1,4 +1,4 @@ -package com.github.johnnyjayjay.spigotmaps; +package com.github.johnnyjayjay.spigotmaps.util; import java.awt.Point; diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/util/ImageTools.java similarity index 99% rename from src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java rename to src/main/java/com/github/johnnyjayjay/spigotmaps/util/ImageTools.java index d025d9d..233daf8 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/ImageTools.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/util/ImageTools.java @@ -1,4 +1,4 @@ -package com.github.johnnyjayjay.spigotmaps; +package com.github.johnnyjayjay.spigotmaps.util; import com.github.johnnyjayjay.spigotmaps.rendering.GifImage; import com.github.johnnyjayjay.spigotmaps.rendering.TextRenderer; From 814a4064a5802f8ecee04f358b2bc8e0bd6c1198 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Sat, 20 Apr 2019 00:15:16 +0200 Subject: [PATCH 09/17] Fix ImageTools cropping --- .../spigotmaps/util/ImageTools.java | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/util/ImageTools.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/util/ImageTools.java index 233daf8..49da84f 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/util/ImageTools.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/util/ImageTools.java @@ -105,7 +105,7 @@ public static BufferedImage resizeToMapSize(BufferedImage image) { * @param gif the non-{@code null} {@link GifImage} to be divided. * @param crop true, if each frame should be cropped to a square part in the middle (i.e. the image will not be * resized) or false, if each frame should be resized (i.e. the whole image will be visible, - * but compressed to 1:1). + * but compressed to 1:1). Note that it still might be resized if it is too small. * @return a never-null List containing the parts. Empty if the gif does not have any frames. */ public static List divideIntoMapSizedParts(GifImage gif, boolean crop) { @@ -121,8 +121,8 @@ public static List divideIntoMapSizedParts(GifImage gif, boolean crop) ); } - int parts = newFrames[0].getImage().getWidth() / MINECRAFT_MAP_SIZE.width * 2; - GifImage.Frame[][] dividedParts = new GifImage.Frame[parts][]; + int parts = newFrames[0].getImage().getWidth() / MINECRAFT_MAP_SIZE.width; + GifImage.Frame[][] dividedParts = new GifImage.Frame[parts][newFrames.length]; for (int i = 0; i < newFrames.length; i++) { GifImage.Frame frame = newFrames[i]; BufferedImage[] imageParts = divideIntoParts(frame.getImage()); @@ -144,7 +144,7 @@ public static List divideIntoMapSizedParts(GifImage gif, boolean crop) * @param image the non-{@code null} image to be divided. * @param crop true, if the image should be cropped to a square part in the middle (i.e. the image will not be * resized) or false, if the image should be resized (i.e. the whole image will be visible, - * but compressed to 1:1). + * but compressed to 1:1). Note that it still might be resized if it is too small. * @return a never-null List containing the parts. */ public static List divideIntoMapSizedParts(BufferedImage image, boolean crop) { @@ -154,13 +154,13 @@ public static List divideIntoMapSizedParts(BufferedImage image, b private static BufferedImage[] divideIntoParts(BufferedImage image) { Dimension partSize = MINECRAFT_MAP_SIZE; int linearParts = image.getWidth() / partSize.width; - BufferedImage[] result = new BufferedImage[linearParts * 2]; + List result = new ArrayList<>(linearParts * linearParts); for (int i = 0; i < linearParts; i++) { for (int j = 0; j < linearParts; j++) { - result[i] = image.getSubimage(partSize.width * j, partSize.height * i, partSize.width, partSize.height); + result.add(image.getSubimage(partSize.width * i, partSize.height * i, partSize.width, partSize.height)); } } - return result; + return result.toArray(new BufferedImage[0]); } private static BufferedImage scaleToMapDividableSquare(BufferedImage image) { @@ -177,10 +177,19 @@ private static BufferedImage scaleToMapDividableSquare(BufferedImage image) { private static BufferedImage cropToMapDividableSquare(BufferedImage image) { int width = image.getWidth(); int height = image.getHeight(); - int measure = width > height - ? MINECRAFT_MAP_SIZE.width * (width / MINECRAFT_MAP_SIZE.width) - : MINECRAFT_MAP_SIZE.height * (height / MINECRAFT_MAP_SIZE.height); - return copyOf(image).getSubimage(width - measure / 2, height - measure / 2, measure, measure); + Dimension size = MINECRAFT_MAP_SIZE; + if (width < size.width && height < size.height) { + return resizeToMapSize(image); + } else if (width < size.width) { + return resizeToMapSize(image.getSubimage(0, (height - size.height) / 2, size.width, size.height)); + } else if (height < size.height) { + return resizeToMapSize(image.getSubimage((width - size.width) / 2, 0, size.width, size.height)); + } else { + int measure = width < height + ? size.width * (width / size.width) + : size.height * (height / size.height); + return copyOf(image).getSubimage((width - measure) / 2, (height - measure) / 2, measure, measure); + } } /** From 17a04e1cdae14e5db8d37df84eab1ae8904dffdb Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Sat, 20 Apr 2019 12:09:02 +0200 Subject: [PATCH 10/17] Fix and add gif to readme --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2ef9a57..288c92e 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # Spigot-Maps -[![Release](https://jitpack.io/v/User/Repo.svg)] +![Release](https://jitpack.io/v/JohnnyJayJay/spigot-maps.svg) + [JavaDoc](https://javadoc.jitpack.io/com/github/johnnyjayjay/spigot-maps/1.0/javadoc/index.html) A small library that makes the use of customised maps in Spigot very easy. ## Features - Dynamic map rendering (based on render context) -- Text and image rendering with convenient usage +- Text, image and animated gif rendering with convenient usage - Base class for own renderer implementations - API to store renderers persistently - Tools to resize / crop / divide images so that they fit the minecraft maps From 6f82951f40c7e6ef07f753a6b8fa1a80fd9e0f70 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Sat, 20 Apr 2019 14:33:54 +0200 Subject: [PATCH 11/17] Restructure inheritance of text renderers, add AnimatedTextRenderer, rename TextRenderer to SimpleTextRenderer, add second createItemStack method in RenderedMap with more metadata, improve documentation --- .../johnnyjayjay/spigotmaps/MapBuilder.java | 4 +- .../johnnyjayjay/spigotmaps/RenderedMap.java | 20 ++- .../rendering/AbstractMapRenderer.java | 13 +- .../rendering/AnimatedTextRenderer.java | 141 ++++++++++++++++++ .../spigotmaps/rendering/ImageRenderer.java | 1 + .../rendering/SimpleTextRenderer.java | 81 ++++++++++ .../spigotmaps/rendering/TextRenderer.java | 114 +++++--------- .../spigotmaps/util/ImageTools.java | 4 +- 8 files changed, 294 insertions(+), 84 deletions(-) create mode 100644 src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AnimatedTextRenderer.java create mode 100644 src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/SimpleTextRenderer.java diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/MapBuilder.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/MapBuilder.java index cbf0f57..2998f54 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/MapBuilder.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/MapBuilder.java @@ -2,7 +2,7 @@ import com.github.johnnyjayjay.spigotmaps.rendering.AbstractMapRenderer; import com.github.johnnyjayjay.spigotmaps.rendering.ImageRenderer; -import com.github.johnnyjayjay.spigotmaps.rendering.TextRenderer; +import com.github.johnnyjayjay.spigotmaps.rendering.SimpleTextRenderer; import com.github.johnnyjayjay.spigotmaps.util.Checks; import org.bukkit.Bukkit; import org.bukkit.World; @@ -75,7 +75,7 @@ public MapBuilder world(World world) { * * @see AbstractMapRenderer * @see ImageRenderer - * @see TextRenderer + * @see SimpleTextRenderer * @param renderers A non-null list of renderers. * @return this. */ diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/RenderedMap.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/RenderedMap.java index 4f49009..067e9e4 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/RenderedMap.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/RenderedMap.java @@ -9,6 +9,7 @@ import org.bukkit.map.MapRenderer; import org.bukkit.map.MapView; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.stream.Stream; @@ -186,13 +187,28 @@ public void setWorld(World world) { /** * Creates and returns an {@link ItemStack} of the type {@code Material.MAP} associated with this instance's - * underlying {@link MapView}. + * underlying {@link MapView} and no further metadata. * - * @return a freshly created, never-{@code null} ItemStack. + * @return a never-{@code null} ItemStack. + * @see #createItemStack(String, String...) */ public ItemStack createItemStack() { + return createItemStack(null); + } + + /** + * Creates and returns an {@link ItemStack} of the type {@code Material.MAP} associated with this instance's + * underlying {@link MapView}. + * + * @param displayName the display name of the result. + * @param lore the lore of the result or nothing, if there shouldn't be lore. + * @return a new ItemStack. + */ + public ItemStack createItemStack(String displayName, String... lore) { MapMeta mapMeta = (MapMeta) Bukkit.getItemFactory().getItemMeta(Material.MAP); mapMeta.setMapView(view); + mapMeta.setDisplayName(displayName); + mapMeta.setLore(lore.length == 0 ? null : Arrays.asList(lore)); ItemStack itemStack = new ItemStack(Material.MAP); itemStack.setItemMeta(mapMeta); return itemStack; diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java index 8bf3530..f9f8a06 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java @@ -23,7 +23,7 @@ * * @author Johnny_JayJay (https://www.github.com/JohnnyJayJay) * @see ImageRenderer - * @see TextRenderer + * @see SimpleTextRenderer */ public abstract class AbstractMapRenderer extends MapRenderer { @@ -136,6 +136,15 @@ public void stopRendering() { this.stop = true; } + /** + * Returns whether this renderer has stopped rendering as a result to a call to {@link #stopRendering()}. + * + * @return {@code true}, if it has stopped. + */ + public boolean isStopped() { + return stop; + } + /** * Renders the map after the preconditions have passed, i.e.: *
    @@ -179,7 +188,7 @@ protected static abstract class Builder> { * Checks whether the precondition is null or the starting point doesn't fit the minecraft map size. * Should be called before building instances of {@link AbstractMapRenderer}. */ - protected final void check() { + protected void check() { Checks.checkNotNull(precondition, "Precondition"); Checks.checkStartingPoint(startingPoint); } diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AnimatedTextRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AnimatedTextRenderer.java new file mode 100644 index 0000000..47cf731 --- /dev/null +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AnimatedTextRenderer.java @@ -0,0 +1,141 @@ +package com.github.johnnyjayjay.spigotmaps.rendering; + +import com.github.johnnyjayjay.spigotmaps.util.Checks; +import org.bukkit.entity.Player; +import org.bukkit.map.MapFont; + +import java.awt.Point; +import java.util.Set; +import java.util.function.Predicate; + +/** + * An implementation of {@link TextRenderer} that renders texts onto a map character by character. + *

    + * This class is not thread safe and will stop rendering automatically once the given text has fully rendered. + * + * @author Johnny_JayJay (https://www.github.com/JohnnyJayJay) + * @see Builder + */ +public class AnimatedTextRenderer extends TextRenderer { + + private StringBuilder renderedText; + private int currentChar; + private int charsPerSecond; + private long ticksToWait; + + private AnimatedTextRenderer( + Point startingPoint, + Set receivers, + Predicate precondition, + CharSequence text, + MapFont font, + int charsPerSecond, + int tickDelay + ) { + super(startingPoint, receivers, false, precondition, text, font); + this.charsPerSecond = charsPerSecond; + this.currentChar = 0; + this.renderedText = new StringBuilder(); + this.ticksToWait = tickDelay + 1; + } + + @Override + protected void render(RenderContext context) { + if (--ticksToWait > 0) + return; + + if (currentChar >= text.length()) { + stopRendering(); + } else { + renderedText.append(text.charAt(currentChar++)); + context.getCanvas().drawText(startingPoint.x, startingPoint.y, font, renderedText.toString()); + this.ticksToWait = Math.round(Math.pow(charsPerSecond / 20D, -1)); + } + } + + /** + * Returns how many characters are rendered each second by this class. + */ + public int getCharsPerSecond() { + return charsPerSecond; + } + + /** + * Sets how many characters are rendered each second. + * + * @param charsPerSecond the new amount. + * @throws IllegalArgumentException if the argument is not positive. + */ + public void setCharsPerSecond(int charsPerSecond) { + Checks.check(charsPerSecond > 0, "Chars per second must be positive"); + this.charsPerSecond = charsPerSecond; + } + + /** + * Creates and returns a new instance of this class' {@link Builder}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder class used to create instances of the enclosing {@link AnimatedTextRenderer} class. + * + * @author Johnny_JayJay (https://github.com/johnnyjayjay) + * @see #builder() + */ + public static class Builder extends TextRenderer.Builder { + + private int charsPerSecond = 20; + private int delay = 0; + + private Builder() { + } + + @Override + public AnimatedTextRenderer build() { + super.check(); + Checks.check(charsPerSecond > 0, "Chars per second must be positive"); + Checks.check(delay >= 0, "Delay must not be negative"); + return new AnimatedTextRenderer(startingPoint, receivers, precondition, text, font, charsPerSecond, delay); + } + + /** + * Sets the amount of characters that should be rendered each second. + *

    + * This is optional. The default value is 20, i.e. 20 characters will be rendered each second. + * + * @param amount a positive int amount. + * @return this. + */ + public Builder charsPerSecond(int amount) { + this.charsPerSecond = amount; + return this; + } + + /** + * Sets a delay in ticks that should be applied to this renderer, i.e. + * it will delay the animation for the time of this delay at the beginning. + *

    + * This is optional. The default value is 0, i.e. there won't be a delay. + * + * @param ticks the amount of ticks to wait (20 ticks = 1 second). + * @return this. + */ + public Builder delay(int ticks) { + this.delay = ticks; + return this; + } + + /** + * Not a supported operation, because every AnimatedTextRenderer MUST render more than once and this + * value can therefore not be set individually. + * + * @throws UnsupportedOperationException always. + */ + @Override + public Builder renderOnce(boolean renderOnce) { + throw new UnsupportedOperationException("renderOnce is always false for AnimatedTextRenderers and thus not allowed to be set"); + } + } +} diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java index e959153..e8d252f 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/ImageRenderer.java @@ -89,6 +89,7 @@ public static Builder builder() { /** * A builder class used to create instances of the enclosing {@link ImageRenderer} class. * + * @see #builder() * @author Johnny_JayJay (https://github.com/johnnyjayjay) */ public static class Builder extends AbstractMapRenderer.Builder { diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/SimpleTextRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/SimpleTextRenderer.java new file mode 100644 index 0000000..4a7966f --- /dev/null +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/SimpleTextRenderer.java @@ -0,0 +1,81 @@ +package com.github.johnnyjayjay.spigotmaps.rendering; + +import org.bukkit.entity.Player; +import org.bukkit.map.MapFont; + +import java.awt.Point; +import java.util.Set; +import java.util.function.Predicate; + +/** + * An implementation of {@link TextRenderer} that can be used to render text on a map. + * + * @author Johnny_JayJay (https://www.github.com/JohnnyJayJay) + * @see Builder + */ +public class SimpleTextRenderer extends TextRenderer { + + private SimpleTextRenderer( + Point startingPoint, + Set receivers, + Predicate precondition, + boolean renderOnce, + String text, + MapFont font + ) { + super(startingPoint, receivers, renderOnce, precondition, text, font); + } + + @Override + protected void render(RenderContext context) { + context.getCanvas().drawText(startingPoint.x, startingPoint.y, font, text.toString()); + } + + /** + * Creates a {@link SimpleTextRenderer} that renders the given lines of text onto a map. + * + * @param lines 0-n Strings or an array of Strings to use as the lines. Must not be {@code null}. + * @return a new, never-null instance of {@link SimpleTextRenderer}. + */ + public static SimpleTextRenderer create(String... lines) { + return builder().addLines(lines).build(); + } + + /** + * Creates and returns a new instance of this class' {@link Builder}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder class used to create instances of the enclosing {@link SimpleTextRenderer} class. + * + * @author Johnny_JayJay (https://github.com/johnnyjayjay) + * @see #builder() + */ + public static class Builder extends TextRenderer.Builder { + + private Builder() { + } + + /** + * Builds a new instance of {@link SimpleTextRenderer} based on the settings made. + * + * @return a never-null instance of {@link SimpleTextRenderer}. + * @throws IllegalArgumentException if: + *

      + *
    • The precondition is {@code null}
    • + *
    • The font is {@code null}
    • + *
    • The starting point is {@code null}
    • + *
    • The starting point's coordinates are not positive
    • + *
    • The starting point's coordinates are out of the minecraft map size bounds
    • + *
    + */ + @Override + public SimpleTextRenderer build() { + super.check(); + return new SimpleTextRenderer(startingPoint, receivers, precondition, renderOnce, text.toString(), font); + } + } +} diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java index a9f09c8..34ae087 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java @@ -6,29 +6,23 @@ import org.bukkit.map.MinecraftFont; import java.awt.Point; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; import java.util.Set; import java.util.function.Predicate; /** - * An implementation of {@link AbstractMapRenderer} that can be used to render text on a map. - * * @author Johnny_JayJay (https://www.github.com/JohnnyJayJay) - * @see Builder */ -public class TextRenderer extends AbstractMapRenderer { +public abstract class TextRenderer extends AbstractMapRenderer { - private String text; - private MapFont font; + protected CharSequence text; + protected MapFont font; - private TextRenderer( + protected TextRenderer( + Point startingPoint, Set receivers, - Predicate precondition, boolean renderOnce, - String text, - Point startingPoint, + Predicate precondition, + CharSequence text, MapFont font ) { super(startingPoint, receivers, renderOnce, precondition); @@ -36,23 +30,11 @@ private TextRenderer( this.font = font; } - @Override - protected void render(RenderContext context) { - context.getCanvas().drawText(startingPoint.x, startingPoint.y, font, text); - } - /** * Returns the text that this renderer renders, including new lines {@code \n}. */ public String getText() { - return text; - } - - /** - * Returns the font used to render the given text. - */ - public MapFont getFont() { - return font; + return text.toString(); } /** @@ -61,10 +43,18 @@ public MapFont getFont() { * @param text a new text String. New lines must be included if needed. * @throws IllegalArgumentException if the argument is {@code null}. */ - public void setText(String text) { + public void setText(CharSequence text) { Checks.checkNotNull(text, "Text"); this.text = text; } + + /** + * Returns the font used to render the given text. + */ + public MapFont getFont() { + return font; + } + /** * Sets the font the rendered text should use. * @@ -77,79 +67,51 @@ public void setFont(MapFont font) { } /** - * Creates a {@link TextRenderer} that renders the given lines of text onto a map. - * - * @param lines 0-n Strings or an array of Strings to use as the lines. Must not be {@code null}. - * @return a new, never-null instance of {@link TextRenderer}. - */ - public static TextRenderer create(String... lines) { - return builder().addLines(lines).build(); - } - - /** - * Creates and returns a new instance of this class' {@link Builder}. - */ - public static Builder builder() { - return new Builder(); - } - - /** - * A builder class used to create instances of the enclosing {@link TextRenderer} class. + * A base builder class for every extension of {@link TextRenderer}. * * @author Johnny_JayJay (https://github.com/johnnyjayjay) */ - public static class Builder extends AbstractMapRenderer.Builder { + protected static abstract class Builder> + extends AbstractMapRenderer.Builder { - private List lines = new ArrayList<>(); - private Point startingPoint = new Point(); - private MapFont font = MinecraftFont.Font; - - private Builder() { - } + protected final StringBuilder text = new StringBuilder(); + protected MapFont font = MinecraftFont.Font; /** - * Builds a new instance of {@link TextRenderer} based on the settings made. - * - * @return a never-null instance of {@link TextRenderer}. - * @throws IllegalArgumentException if: - *
      - *
    • The precondition is {@code null}
    • - *
    • The font is {@code null}
    • - *
    • The starting point is {@code null}
    • - *
    • The starting point's coordinates are not positive
    • - *
    • The starting point's coordinates are out of the minecraft map size bounds
    • - *
    + * Makes the checks from {@link AbstractMapRenderer.Builder#check()} and additionally checks if the font is {@code null}. */ @Override - public TextRenderer build() { + protected void check() { super.check(); Checks.checkNotNull(font, "Font"); - return new TextRenderer(receivers, precondition, renderOnce, String.join("\n", lines), startingPoint, font); } /** - * Adds a {@link List} of lines (Strings) to the text this renderer will draw. + * Adds zero or more lines (Strings) to the text this renderer will draw *

    * Not adding any lines is a valid option and will result in a map without text. * - * @param lines a list of Strings. Must not be {@code null}. + * @param lines 0-n Strings or an array of Strings. Must not be {@code null}. * @return this. */ - public Builder addLines(List lines) { - this.lines.addAll(lines); - return this; + public U addLines(CharSequence... lines) { + for (CharSequence line : lines) + text.append(line).append('\n'); + return (U) this; } /** - * Adds zero or more lines (Strings) to the text this renderer will draw + * Adds an {@link Iterable} of lines (Strings) to the text this renderer will draw. *

    * Not adding any lines is a valid option and will result in a map without text. * - * @param lines 0-n Strings or an array of Strings. Must not be {@code null}. + * @param lines an Iterable of Strings, e.g. a List. Must not be {@code null}. * @return this. */ - public Builder addLines(String... lines) { - return addLines(Arrays.asList(lines)); + public U addLines(Iterable lines) { + for (CharSequence line : lines) + text.append(line).append('\n'); + return (U) this; } /** @@ -160,9 +122,9 @@ public Builder addLines(String... lines) { * @param font a {@link MapFont} to use as a font for the text. * @return this. */ - public Builder font(MapFont font) { + public U font(MapFont font) { this.font = font; - return this; + return (U) this; } } } diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/util/ImageTools.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/util/ImageTools.java index 49da84f..b2ef7fd 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/util/ImageTools.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/util/ImageTools.java @@ -1,7 +1,7 @@ package com.github.johnnyjayjay.spigotmaps.util; import com.github.johnnyjayjay.spigotmaps.rendering.GifImage; -import com.github.johnnyjayjay.spigotmaps.rendering.TextRenderer; +import com.github.johnnyjayjay.spigotmaps.rendering.SimpleTextRenderer; import javax.imageio.ImageIO; import java.awt.Color; @@ -52,7 +52,7 @@ public static BufferedImage loadWithUserAgentFrom(URL url) throws IOException { /** * Creates a {@link BufferedImage} with the size of {@link #MINECRAFT_MAP_SIZE}. - * The whole image will have one color. This can be used as a background for {@link TextRenderer}s, for example. + * The whole image will have one color. This can be used as a background for {@link SimpleTextRenderer}s, for example. * * @param color the non-{@code null} {@link Color} this image will have. * @return a never-null image. From 6cd557259f8744160b4b6cc1405fb3921cd59603 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Sat, 20 Apr 2019 15:43:07 +0200 Subject: [PATCH 12/17] add examples for changes in readme --- README.md | 95 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 288c92e..22cd479 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A small library that makes the use of customised maps in Spigot very easy. ## Features - Dynamic map rendering (based on render context) -- Text, image and animated gif rendering with convenient usage +- (Animated) Text, image and animated gif rendering with convenient usage - Base class for own renderer implementations - API to store renderers persistently - Tools to resize / crop / divide images so that they fit the minecraft maps @@ -68,7 +68,7 @@ ImageRenderer catRenderer = ImageRenderer.builder() .build(); // build the instance Dimension mapSize = ImageTools.MINECRAFT_MAP_SIZE; // the dimensions of a Minecraft map (in pixels) -TextRenderer messageRenderer = TextRenderer.builder() +SimpleTextRenderer messageRenderer = SimpleTextRenderer.builder() .addLines("Cats", "are", "so", "cute") // set the lines that will be drawn onto the map .addPlayers(player1, player2) .font(myFont) // set a text font @@ -86,6 +86,19 @@ ItemStack mapItem = map.createItemStack(); // get an ItemStack from this map to This example would result in a map that has an image of a cat in the background and the text "Cats are so cute" in the foreground. +You can still mutate the renderers afterwards: +```java +messageRenderer.setText("Cats\nare\nstill\ncute"); +``` +Note that this will only have effect if `renderOnce` is set to true while building. +The reason for that decision is performance. Only rendering once saves resources, but +disables later modification. +If you want to mutate a renderer after building but then leave it that way, you may call +```java +messageRenderer.stopRendering(); +``` +to save the resources. + ### Quicker ways There are even quicker ways to accomplish some of these operations. @@ -107,7 +120,7 @@ See `ImageTools.createSingleColoredImage(Color)` to get an instance of `Buffered **Create a TextRenderer with just some lines of text**: ```java -TextRenderer renderer = TextRenderer.create("This", "is", "noice"); +SimpleTextRenderer renderer = SimpleTextRenderer.create("This", "is", "noice"); ``` **Create a RenderedMap with just some MapRenderers**: @@ -118,33 +131,73 @@ RenderedMap map = RenderedMap.create(renderer1, renderer2); // not providing any ### Advanced -Images that take more than 1 map to display can be created using `ImageTools.resizeIntoMapSizedParts(BufferedImage, boolean)`. This uses an algorithm that makes a square version of the image first. How this is done can be determined via the second `boolean` parameter. If it is set to `true`, a square cropped out from the middle of the image will be used. If it is `false`, the whole image will be resized to 1:1. +#### Gifs +This library can handle animated gifs using `GifRenderer` and `GifImage`. +Instances of `GifImage` can be obtained via an instance of `GifDecoder` from this library's dependencies: ```java -BufferedImage[][] parts = ImageTools.resizeIntoMapSizedParts(image, true); +GifDecoder decoder = new GifDecoder(); +decoder.read("./example.gif"); // this also works with URLs, InputStreams etc. +GifImage gif = GifImage.fromDecoder(decoder); ``` +For more info about `GifDecoder`, look at [this](https://github.com/rtyley/animated-gif-lib-for-java). -To turn these into map items: +You can also implement an algorithm to decode gifs yourself and then make use of `GifImage#create(List)`. +`GifRenderer`s are created like any other renderer: ```java -ItemStack[][] maps = Arrays.stream(parts).map( - (part) -> Arrays.stream(part) - .map(ImageRenderer::create) - .map(RenderedMap::create) - .map(RenderedMap::createItemStack) - .toArray(ItemStack[]::new) -).toArray(ItemStack[][]::new); +GifRenderer renderer = GifRenderer.builder() + .gif(gif) // set the GifImage we just created + .repeat(5) // repeat the gif 5 times: to repeat it indefinitely, omit this setting or set it to GifRenderer.REPEAT_FOREVER + .build(); ``` +This renderer stops rendering automatically after 5 repetitions and +can now be added to a `MapView` / `RenderedMap` as shown above. -This results in the same as: +#### Animated Text +Text doesn't have to be static. This library provides a map renderer that renders text character by character. ```java -ItemStack[][] maps = new ItemStack[parts.length][]; -for (int row = 0; row < parts.length; row++) { - for (int column = 0; column < parts[row].length, column++) { - maps[row][column] = RenderedMap.create(ImageRenderer - .create(parts[row][column])) - .createItemStack(); - } +AnimatedTextRenderer renderer = AnimatedTextRenderer.builder() + .addText("This text will appear char by char.") + .charsPerSecond(10) // 10 characters should appear each second + .delay(20) // start the animation after 20 ticks (1 second) + .build(); +``` +This renderer automatically stops rendering after having finished. + +#### Splitting images + +Images that take more than 1 map to display can be created using `ImageTools.divideIntoMapSizedParts(BufferedImage, boolean)`. +This uses an algorithm that makes a square version of the image first. How this is done can be determined via the second +`boolean` parameter. If it is set to `true`, a square cropped out from the middle of the image will be used +(if the image is big enough). If it is `false`, the whole image will be resized to 1:1. + +```java +List parts = ImageTools.divideIntoMapSizedParts(image, true); +``` + +**The exact same methods are available to `GifImage`s.** + +To turn these into map items: + +```java +for (BufferedImage part : parts) { + ImageRenderer renderer = ImageRenderer.create(part); + ItemStack mapItem = RenderedMap.create(renderer).createItemStack(); + // do something with it } ``` + +Or, if you want to do it stream-like: +```java +parts.stream() + .map(ImageRenderer::create) + .map(RenderedMap::create) + .map(RenderedMap::createItemStack) + .forEach((mapItem) -> { + // do something with it +}) +``` + + From 24924ba1eff20c57a450ef93b074c2067fb5fbfd Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Sat, 20 Apr 2019 15:43:40 +0200 Subject: [PATCH 13/17] Add addText method in TextRenderer.Builder --- .../spigotmaps/rendering/TextRenderer.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java index 34ae087..4459735 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java @@ -114,6 +114,18 @@ public U addLines(Iterable lines) { return (U) this; } + /** + * Adds a {@link CharSequence} to the text this renderer will draw. + * + * @param text A CharSequence, e.g. a String to add. This must include new line characters + * if the result should be multiple lines. + * @return this. + */ + public U addText(CharSequence text) { + this.text.append(text); + return (U) this; + } + /** * Sets the font to use for the text. *

    From 5439c5d1c494fbb3c523f490d15caab79b1dd949 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Sat, 20 Apr 2019 16:35:45 +0200 Subject: [PATCH 14/17] Fix AnimatedTextRenderer algorithm --- .../rendering/AnimatedTextRenderer.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AnimatedTextRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AnimatedTextRenderer.java index 47cf731..1498c4c 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AnimatedTextRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AnimatedTextRenderer.java @@ -39,6 +39,17 @@ private AnimatedTextRenderer( this.ticksToWait = tickDelay + 1; } + private double calculateTicksToWait() { + double charsPerTick = charsPerSecond / 20D; + return (currentChar + 1) / charsPerTick - currentChar / charsPerTick; + } + + private int charsToAppend(double ticksToWait) { + return ticksToWait < 1 + ? (int) (1 / ticksToWait) + : 1; + } + @Override protected void render(RenderContext context) { if (--ticksToWait > 0) @@ -47,9 +58,13 @@ protected void render(RenderContext context) { if (currentChar >= text.length()) { stopRendering(); } else { - renderedText.append(text.charAt(currentChar++)); + double ticksToWait = calculateTicksToWait(); + this.ticksToWait = ticksToWait < 1 ? 1 : Math.round(ticksToWait); + for (int destinationLength = charsToAppend(ticksToWait) + currentChar; + currentChar < destinationLength; currentChar++) { + renderedText.append(text.charAt(currentChar)); + } context.getCanvas().drawText(startingPoint.x, startingPoint.y, font, renderedText.toString()); - this.ticksToWait = Math.round(Math.pow(charsPerSecond / 20D, -1)); } } From 67b13bfe32c5a21b23968854bd7f10a97bbdcc91 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Sun, 21 Apr 2019 14:49:22 +0200 Subject: [PATCH 15/17] Fix generic type --- .../johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java index f9f8a06..c43089c 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/AbstractMapRenderer.java @@ -167,7 +167,7 @@ public boolean isStopped() { * @author Johnny_JayJay (https://www.github.com/JohnnyJayJay) */ @SuppressWarnings("unchecked") - protected static abstract class Builder> { + protected static abstract class Builder> { protected final Set receivers = new HashSet<>(); protected Predicate precondition = (ctx) -> true; From 642dedfc20f3a7b75a03d1d7633fef925ad1e425 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Wed, 24 Apr 2019 19:39:58 +0200 Subject: [PATCH 16/17] Add MapStorage to readme, adjust javadoc link, add @SuppressWarnings for TextRenderer.Builder --- README.md | 24 ++++++++++++++++++- build.gradle | 2 ++ .../spigotmaps/rendering/TextRenderer.java | 1 + 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 22cd479..7ca6804 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ![Release](https://jitpack.io/v/JohnnyJayJay/spigot-maps.svg) -[JavaDoc](https://javadoc.jitpack.io/com/github/johnnyjayjay/spigot-maps/1.0/javadoc/index.html) +[JavaDoc](https://javadoc.jitpack.io/com/github/johnnyjayjay/spigot-maps/1.1/javadoc/index.html) A small library that makes the use of customised maps in Spigot very easy. @@ -200,4 +200,26 @@ parts.stream() }) ``` +#### Using MapStorage +The `MapStorage` API makes it possible to save renderers persistently. To utilise it, you have to implement MapStorage: + +```java +public class FileStorage implements MapStorage { + + @Override + public void store(int id, MapRenderer renderer) { + // serialize the renderer associated with the given id + } + + @Override + public boolean remove(int id, MapRenderer renderer) { + // remove the given renderer's association with the given id + } + + @Override + public List provide(int id) { + // fetch / deserialize / read all renderers stored for the given id + } +} +``` \ No newline at end of file diff --git a/build.gradle b/build.gradle index a43b5e5..e1657fd 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,7 @@ plugins { id 'java' id 'maven' + id 'com.github.johnrengelman.shadow' version '4.0.4' } group 'com.github.johnnyjayjay' @@ -36,6 +37,7 @@ task javadocJar(type: Jar, dependsOn: javadoc) { artifacts { archives sourcesJar archives javadocJar + archives shadowJar } diff --git a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java index 4459735..f6a9846 100644 --- a/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java +++ b/src/main/java/com/github/johnnyjayjay/spigotmaps/rendering/TextRenderer.java @@ -71,6 +71,7 @@ public void setFont(MapFont font) { * * @author Johnny_JayJay (https://github.com/johnnyjayjay) */ + @SuppressWarnings("unchecked") protected static abstract class Builder> extends AbstractMapRenderer.Builder { From 9c61d4129251cc5c6b57766b66c116873394fac1 Mon Sep 17 00:00:00 2001 From: JohnnyJayJay Date: Wed, 24 Apr 2019 19:43:51 +0200 Subject: [PATCH 17/17] adjust build.gradle --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index e1657fd..1856c7f 100644 --- a/build.gradle +++ b/build.gradle @@ -34,6 +34,8 @@ task javadocJar(type: Jar, dependsOn: javadoc) { from javadoc.destinationDir } +shadowJar.classifier = "withDependencies" + artifacts { archives sourcesJar archives javadocJar