diff --git a/paper-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java b/paper-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java index a5e98571d6d8..94c0e6d748ce 100644 --- a/paper-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java +++ b/paper-server/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java @@ -3,6 +3,7 @@ import com.google.common.base.Preconditions; import java.awt.Color; import java.awt.Image; +import java.awt.image.BufferedImage; import java.util.Arrays; import org.bukkit.map.MapCanvas; import org.bukkit.map.MapCursorCollection; @@ -92,22 +93,33 @@ protected byte[] getBuffer() { @Override public void drawImage(int x, int y, Image image) { // Paper start - Reduce work done by limiting size of image and using System.arraycopy - int width = 128 - x; - int height = 128 - y; - if (image.getHeight(null) < height) - height = image.getHeight(null); + final int imageWidth = image.getWidth(null); + final int imageHeight = image.getHeight(null); + + // The source x value *may* be negative, meaning we'd need to "offset" the source image before drawing it. + final int sourceX = Math.max(-x, 0); + final int sourceY = Math.max(-y, 0); + final int destX = Math.max(x, 0); + final int destY = Math.max(y, 0); + + // The effective width/height to draw on the canvas. + final int effectiveWidth = Math.min(imageWidth - sourceX, 128 - destX); + final int effectiveHeight = Math.min(imageHeight - sourceY, 128 - destY); + + if (effectiveWidth <= 0 || effectiveHeight <= 0) + return; // Create a subimage if the image is larger than the max allowed size - java.awt.image.BufferedImage temp; - if (image.getWidth(null) >= width && image instanceof java.awt.image.BufferedImage bImage) { + BufferedImage temp; + if (imageWidth >= effectiveWidth && image instanceof BufferedImage bImage) { // If the image is larger than the max allowed size, get a subimage, otherwise use the image as is - if (image.getWidth(null) > width || image.getHeight(null) > height) { - temp = bImage.getSubimage(0, 0, width, height); + if (imageWidth > effectiveWidth || imageHeight > effectiveHeight) { + temp = bImage.getSubimage(sourceX, sourceY, effectiveWidth, effectiveHeight); } else { temp = bImage; } } else { - temp = new java.awt.image.BufferedImage(width, height, java.awt.image.BufferedImage.TYPE_INT_ARGB); + temp = new BufferedImage(effectiveWidth, effectiveHeight, BufferedImage.TYPE_INT_ARGB); java.awt.Graphics2D graphics = temp.createGraphics(); graphics.drawImage(image, 0, 0, null); graphics.dispose(); @@ -117,14 +129,20 @@ public void drawImage(int x, int y, Image image) { // Since we now control the size of the image, we can safely use System.arraycopy // If x is 0, we can just copy the entire image as width is 128 and height is <=(128-y) - if (x == 0) { - System.arraycopy(bytes, 0, this.buffer, y * 128, width * height); - return; - } + if (x == 0 && effectiveWidth == 128) { // This only works great if the width is 128, otherwise an empty area appears + System.arraycopy(bytes, 0, this.buffer, destY * effectiveWidth, effectiveWidth * effectiveHeight); + } else { + for (int yToCopy = 0; yToCopy < effectiveHeight; ++yToCopy) { + final int src = yToCopy * effectiveWidth; + final int dest = (destY + yToCopy) * 128 + destX; - for (int y2 = 0; y2 < height; ++y2) { - System.arraycopy(bytes, 0, this.buffer, (y + y2) * 128 + x, width); + System.arraycopy(bytes, src, this.buffer, dest, effectiveWidth); + } } + + // Mark all colors within the image as dirty + this.mapView.worldMap.setColorsDirty(destX, destY); + this.mapView.worldMap.setColorsDirty(destX + effectiveWidth - 1, destY + effectiveHeight - 1); // Paper end }