Skip to content

Commit 52fb265

Browse files
authored
Optimize custom map rendering (#11000)
1 parent 1ef4c0e commit 52fb265

File tree

2 files changed

+112
-0
lines changed

2 files changed

+112
-0
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2+
From: Barnaby <22575741+barnabwhy@users.noreply.github.com>
3+
Date: Sat, 29 Jun 2024 12:04:48 +0100
4+
Subject: [PATCH] Optimise color distance check in MapPalette by removing
5+
floating point math
6+
7+
8+
diff --git a/src/main/java/org/bukkit/map/MapPalette.java b/src/main/java/org/bukkit/map/MapPalette.java
9+
index c80faa079eca1564847070f0338fc98024639829..55a46db9d73885ea967ac929013906b687e476bb 100644
10+
--- a/src/main/java/org/bukkit/map/MapPalette.java
11+
+++ b/src/main/java/org/bukkit/map/MapPalette.java
12+
@@ -29,14 +29,19 @@ public final class MapPalette {
13+
}
14+
15+
private static double getDistance(@NotNull Color c1, @NotNull Color c2) {
16+
- double rmean = (c1.getRed() + c2.getRed()) / 2.0;
17+
- double r = c1.getRed() - c2.getRed();
18+
- double g = c1.getGreen() - c2.getGreen();
19+
+ // Paper start - Optimize color distance calculation by removing floating point math
20+
+ int rsum = c1.getRed() + c2.getRed(); // Use sum instead of mean for no division
21+
+ int r = c1.getRed() - c2.getRed();
22+
+ int g = c1.getGreen() - c2.getGreen();
23+
int b = c1.getBlue() - c2.getBlue();
24+
- double weightR = 2 + rmean / 256.0;
25+
- double weightG = 4.0;
26+
- double weightB = 2 + (255 - rmean) / 256.0;
27+
+ // All weights are 512x their original to avoid floating point division
28+
+ int weightR = 1024 + rsum;
29+
+ int weightG = 2048;
30+
+ int weightB = 1024 + (255*2 - rsum);
31+
+
32+
+ // Division by 256 here is unnecessary as this won't change the result of the sort
33+
return weightR * r * r + weightG * g * g + weightB * b * b;
34+
+ // Paper end
35+
}
36+
37+
@NotNull
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2+
From: Barnaby <22575741+barnabwhy@users.noreply.github.com>
3+
Date: Sat, 29 Jun 2024 12:06:51 +0100
4+
Subject: [PATCH] Reduce work done in CraftMapCanvas.drawImage by limiting size
5+
of image and using System.arraycopy instead of for loops and use bitwise
6+
operations to do bounds checks.
7+
8+
9+
diff --git a/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java b/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java
10+
index ff59f759669620795ef355c988b664bdcda39f52..a5e98571d6d83390761c11e28a0bc3c4415799cd 100644
11+
--- a/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java
12+
+++ b/src/main/java/org/bukkit/craftbukkit/map/CraftMapCanvas.java
13+
@@ -91,12 +91,41 @@ public class CraftMapCanvas implements MapCanvas {
14+
15+
@Override
16+
public void drawImage(int x, int y, Image image) {
17+
- byte[] bytes = MapPalette.imageToBytes(image);
18+
- for (int x2 = 0; x2 < image.getWidth(null); ++x2) {
19+
- for (int y2 = 0; y2 < image.getHeight(null); ++y2) {
20+
- this.setPixel(x + x2, y + y2, bytes[y2 * image.getWidth(null) + x2]);
21+
+ // Paper start - Reduce work done by limiting size of image and using System.arraycopy
22+
+ int width = 128 - x;
23+
+ int height = 128 - y;
24+
+ if (image.getHeight(null) < height)
25+
+ height = image.getHeight(null);
26+
+
27+
+ // Create a subimage if the image is larger than the max allowed size
28+
+ java.awt.image.BufferedImage temp;
29+
+ if (image.getWidth(null) >= width && image instanceof java.awt.image.BufferedImage bImage) {
30+
+ // If the image is larger than the max allowed size, get a subimage, otherwise use the image as is
31+
+ if (image.getWidth(null) > width || image.getHeight(null) > height) {
32+
+ temp = bImage.getSubimage(0, 0, width, height);
33+
+ } else {
34+
+ temp = bImage;
35+
}
36+
+ } else {
37+
+ temp = new java.awt.image.BufferedImage(width, height, java.awt.image.BufferedImage.TYPE_INT_ARGB);
38+
+ java.awt.Graphics2D graphics = temp.createGraphics();
39+
+ graphics.drawImage(image, 0, 0, null);
40+
+ graphics.dispose();
41+
}
42+
+
43+
+ byte[] bytes = MapPalette.imageToBytes(temp);
44+
+
45+
+ // Since we now control the size of the image, we can safely use System.arraycopy
46+
+ // If x is 0, we can just copy the entire image as width is 128 and height is <=(128-y)
47+
+ if (x == 0) {
48+
+ System.arraycopy(bytes, 0, this.buffer, y * 128, width * height);
49+
+ return;
50+
+ }
51+
+
52+
+ for (int y2 = 0; y2 < height; ++y2) {
53+
+ System.arraycopy(bytes, 0, this.buffer, (y + y2) * 128 + x, width);
54+
+ }
55+
+ // Paper end
56+
}
57+
58+
@Override
59+
diff --git a/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java b/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java
60+
index 0cbbd915631904fe8c6effefb92895422b33eff6..cf0920e5f84b35647882fb963e9972af4e8427e0 100644
61+
--- a/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java
62+
+++ b/src/main/java/org/bukkit/craftbukkit/map/CraftMapRenderer.java
63+
@@ -23,8 +23,10 @@ public class CraftMapRenderer extends MapRenderer {
64+
@Override
65+
public void render(MapView map, MapCanvas canvas, Player player) {
66+
// Map
67+
- for (int x = 0; x < 128; ++x) {
68+
- for (int y = 0; y < 128; ++y) {
69+
+ // Paper start - Swap inner and outer loops here to (theoretically) improve cache locality
70+
+ for (int y = 0; y < 128; ++y) {
71+
+ for (int x = 0; x < 128; ++x) {
72+
+ // Paper end
73+
canvas.setPixel(x, y, this.worldMap.colors[y * 128 + x]);
74+
}
75+
}

0 commit comments

Comments
 (0)