3939 *
4040 * <strong>Overlay Render Order</strong>
4141 * <p>
42- * Overlays are rendered <strong>in the exact order they are added</strong>.
43- * The first overlay added becomes the bottom layer, and subsequent overlays
42+ * Overlays are rendered <strong>after what layer they are added</strong>.
43+ * The lowest number becomes the bottom layer, and subsequent overlays
4444 * are drawn on top of it. This applies to all overlay types:
4545 * {@link MapColoredPixel}, {@link TextOverlay}, {@link ImageOverlay}, and others.
4646 * </p>
4747 *
48- * <p>Example: calling {@code addImage()} followed by {@code addText()} will render
49- * the image as a background and the text above it.
50- * </p>
5148 *
5249 */
5350public class MapRendererData {
5451 private static int id ;
5552 private final int mapRenderId ;
5653 private final MapRenderer mapRenderer ;
54+ private final Map <Integer , List <MapPixel >> layers = new HashMap <>();
5755 private final List <MapPixel > pixels = new ArrayList <>();
5856 private MapCursorAdapter mapCursors = new MapCursorAdapter ();
5957
@@ -97,36 +95,43 @@ public void setDynamicRenderer(MapRenderHandler handler) {
9795 *
9896 * <p>See {@link MapRendererData} documentation for details on overlay layering.</p>
9997 *
98+ * @param layer the layer you want to set the pixel, higher number means it will be
99+ * rendered higher up.
100100 * @param x The x-coordinate of the pixel.
101101 * @param y The y-coordinate of the pixel.
102102 * @param color The color of the pixel.
103103 */
104- public void addPixel (int x , int y , Color color ) {
105- pixels . add ( new MapColoredPixel (x , y , color ));
104+ public void addPixel (int layer , int x , int y , Color color ) {
105+ this . addPixel ( layer , new MapColoredPixel (x , y , color ));
106106 }
107107
108108 /**
109109 * Adds a colored pixel overlay to the map.
110110 *
111111 * <p>See {@link MapRendererData} documentation for details on overlay layering.</p>
112+ *
113+ * @param layer the layer you want to set the pixel, higher number means it will be
114+ * rendered higher up.
112115 * @param mapColoredPixel The {@link MapColoredPixel} to add.
113116 */
114- public void addPixel (@ Nonnull final MapColoredPixel mapColoredPixel ) {
115- pixels . add ( mapColoredPixel );
117+ public void addPixel (int layer , @ Nonnull final MapColoredPixel mapColoredPixel ) {
118+ this . addMapPixel ( layer , mapColoredPixel );
116119 }
117120
118121 /**
119122 * Adds a text overlay to the map without a custom font character sprite.
120123 *
121124 * <p>See {@link MapRendererData} documentation for details on overlay layering.</p>
122125 *
123- * @param x The x-coordinate of the text.
124- * @param y The y-coordinate of the text.
125- * @param text The text to display.
126+ * @param layer the layer you want to set the text, higher number means it will be
127+ * rendered higher up.
128+ * @param x The x-coordinate of the text.
129+ * @param y The y-coordinate of the text.
130+ * @param text The text to display.
126131 * @return returns the newly created text overlay, so you could set some of the options after.
127132 */
128- public TextOverlay addText (final int x , int y , @ Nonnull final String text ) {
129- return this .addText (x , y , text , null , null );
133+ public TextOverlay addText (int layer , final int x , int y , @ Nonnull final String text ) {
134+ return this .addText (layer , x , y , text , null , null );
130135 }
131136
132137 /**
@@ -135,37 +140,41 @@ public TextOverlay addText(final int x, int y, @Nonnull final String text) {
135140 *
136141 * <p>See {@link MapRendererData} documentation for details on overlay layering.</p>
137142 *
138- * @param x The x-coordinate of the text.
139- * @param y The y-coordinate of the text.
140- * @param text The text to display.
141- * @param font The font for the character.
143+ * @param layer the layer you want to set the text, higher number means it will be
144+ * rendered higher up.
145+ * @param x The x-coordinate of the text.
146+ * @param y The y-coordinate of the text.
147+ * @param text The text to display.
148+ * @param font The font for the character.
142149 * @return returns the newly created text overlay, so you could set some of the options after.
143150 */
144- public TextOverlay addText (final int x , int y , @ Nonnull final String text , @ Nullable final Font font ) {
145- return this .addText (x , y , text , null , font );
151+ public TextOverlay addText (int layer , final int x , int y , @ Nonnull final String text , @ Nullable final Font font ) {
152+ return this .addText (layer , x , y , text , null , font );
146153 }
147154
148155 /**
149156 * Adds a text overlay to the map with a custom font character sprite.
150157 *
151158 * <p>See {@link MapRendererData} documentation for details on overlay layering.</p>
152159 *
160+ * @param layer the layer you want to set the text, higher number means it will be
161+ * rendered higher up.
153162 * @param x The x-coordinate of the text.
154163 * @param y The y-coordinate of the text.
155164 * @param text The text to display.
156165 * @param fontChars Set the characters you want to replace in your text with the font.
157166 * @param font The font for the character
158167 * @return returns the newly created text overlay, so you could set some of the options after.
159168 */
160- public TextOverlay addText (final int x , int y , @ Nonnull final String text , @ Nullable final char [] fontChars , @ Nullable final Font font ) {
169+ public TextOverlay addText (final int layer , final int x , int y , @ Nonnull final String text , @ Nullable final char [] fontChars , @ Nullable final Font font ) {
161170 TextOverlay textOverlay = new TextOverlay (x , y , text );
162171 if (font != null ) {
163172 if (fontChars != null && fontChars .length > 0 ) {
164173 this .fontChars = fontChars ;
165174 }
166175 textOverlay .setMapFont (this .fontChars , font );
167176 }
168- this .addText (textOverlay );
177+ this .addText (layer , textOverlay );
169178 return textOverlay ;
170179 }
171180
@@ -174,10 +183,12 @@ public TextOverlay addText(final int x, int y, @Nonnull final String text, @Null
174183 *
175184 * <p>See {@link MapRendererData} documentation for details on overlay layering.</p>
176185 *
186+ * @param layer the layer you want to set the text, higher number means it will be
187+ * rendered higher up.
177188 * @param textOverlay The {@link TextOverlay} instance to add.
178189 */
179- public void addText (@ Nonnull final TextOverlay textOverlay ) {
180- pixels . add ( textOverlay );
190+ public void addText (final int layer , @ Nonnull final TextOverlay textOverlay ) {
191+ this . addMapPixel ( layer , textOverlay );
181192 }
182193
183194
@@ -202,12 +213,14 @@ public void addText(@Nonnull final TextOverlay textOverlay) {
202213 *
203214 * <p>See {@link MapRendererData} documentation for details on overlay layering.</p>
204215 *
216+ * @param layer the layer you want to set the text, higher number means it will be
217+ * rendered higher up.
205218 * @param x the top-left X position on the map (0–127)
206219 * @param y the top-left Y position on the map (0–127)
207220 * @param image the image to draw at the given position
208221 */
209- public void addImage (final int x , final int y , @ Nonnull final Image image ) {
210- pixels . add ( new ImageOverlay (x , y , image ));
222+ public void addImage (final int layer , final int x , final int y , @ Nonnull final Image image ) {
223+ this . addImage ( layer , new ImageOverlay (x , y , image ));
211224 }
212225
213226 /**
@@ -217,8 +230,6 @@ public void addImage(final int x, final int y, @Nonnull final Image image) {
217230 * occurs inside {@link ImageOverlay}. More advanced preprocessing—such
218231 * as color rebalancing or pixel conversion—is performed only when using
219232 * {@link MapRendererDataCache} instead of adding images directly.
220- * See {@link #addText(TextOverlay)} for additional details about the
221- * caching workflow.
222233 * </p>
223234 *
224235 * <p><strong>Note:</strong> Adding raw images directly may introduce a performance
@@ -228,10 +239,12 @@ public void addImage(final int x, final int y, @Nonnull final Image image) {
228239 *
229240 * <p>See {@link MapRendererData} documentation for details on overlay layering.</p>
230241 *
242+ * @param layer the layer you want to set the text, higher number means it will be
243+ * rendered higher up.
231244 * @param imageOverlay the preconfigured {@link ImageOverlay} to add
232245 */
233- public void addImage (@ Nonnull final ImageOverlay imageOverlay ) {
234- pixels . add ( imageOverlay );
246+ public void addImage (final int layer , @ Nonnull final ImageOverlay imageOverlay ) {
247+ this . addMapPixel ( layer , imageOverlay );
235248 }
236249
237250 /**
@@ -282,39 +295,41 @@ public MapCursorWrapper addCursor(@Nonnull final MapCursorWrapper cursorWrapper)
282295 }
283296
284297 /**
285- * Adds a collection of map overlays (pixels, text, or images) to this renderer.
298+ * Sets a collection of map overlays (pixels, text, or images) to this renderer.
286299 * <p>
287- * This method appends the provided overlays to the existing pixel list
288- * without clearing previous entries. It can be used to add multiple
289- * overlay types at once or to batch-apply preprocessed render data.
300+ * This method appends the provided overlays and replace the existing pixel list.
301+ * It can be used to add multiple overlay types at once or to batch-apply
302+ * preprocessed render data.
290303 * </p>
291304 *
292305 * <p><strong>Note:</strong> For most use cases, prefer the more specific
293- * methods such as {@link #addPixel(int, int, Color)},{@link #addText(int, int, String)}
294- * or {@link #addImage(int, int, Image)} for clarity.</p>
306+ * methods such as {@link #addPixel(int, int, int, Color)} ,{@link #addText(int, int, int, String)} }
307+ * or {@link #addImage(int, int, int, Image)} } for clarity.</p>
295308 *
296309 * <p>See {@link MapRendererData} documentation for details on overlay layering.</p>
297310 *
298- * @param mapPixels the list of {@link MapPixel} instances to add
311+ * @param layer the layer you want to set the text, higher number means it will be
312+ * rendered higher up.
313+ * @param pixels the list of {@link MapPixel} instances to replace the layer wioth
299314 */
300- public void addAll ( List <MapPixel > mapPixels ) {
301- this . pixels . addAll ( mapPixels );
315+ public void replaceLayer ( int layer , List <MapPixel > pixels ) {
316+ layers . put ( layer , new ArrayList <>( pixels ) );
302317 }
303318
304319 /**
305- * Clear the list of set pixels.
320+ * Clear the map of set pixels.
306321 */
307322 public void clear () {
308- this .pixels .clear ();
323+ this .layers .clear ();
309324 }
310325
311326 /**
312- * Returns {@code true} if this list contains no elements .
327+ * Returns {@code true} if this map contains no key-value mappings .
313328 *
314- * @return {@code true} if this list of pixels contains no elements
329+ * @return {@code true} if this map of pixels contains no mappings.
315330 */
316331 public boolean isPixelsEmpty () {
317- return this .pixels .isEmpty ();
332+ return this .layers .isEmpty ();
318333 }
319334
320335 /**
@@ -383,13 +398,29 @@ public MapCursorAdapter getMapCursors() {
383398 return mapCursors ;
384399 }
385400
401+
402+ /**
403+ * Returns the current collection of map pixels.
404+ *
405+ * @return The {@link Map} that contains a list of map pixels set for every layer.
406+ */
407+ public Map <Integer , List <MapPixel >> getLayers () {
408+ return layers ;
409+ }
410+
386411 /**
387412 * Returns the current collection of map pixels.
388413 *
389- * @return The {@link MapCursorAdapter} managing pixels.
414+ * @return The {@link Map} that contains a list of map pixels set for every layer .
390415 */
391416 public List <MapPixel > getPixels () {
392- return pixels ;
417+ List <MapPixel > all = new ArrayList <>();
418+
419+ layers .entrySet ().stream ()
420+ .sorted (Map .Entry .comparingByKey ())
421+ .forEach (e -> all .addAll (e .getValue ()));
422+
423+ return all ;
393424 }
394425
395426 /**
@@ -409,7 +440,7 @@ public void render(@Nonnull MapView map, @Nonnull MapCanvas canvas, @Nonnull Pla
409440 return ;
410441 canvas .setCursors (mapCursors .getMapCursorCollection ());
411442
412- if (!getPixels (). isEmpty ()) {
443+ if (!isPixelsEmpty ()) {
413444 setPixels (canvas );
414445 }
415446 }
@@ -438,7 +469,7 @@ public int hashCode() {
438469 public Map <String , Object > serialize () {
439470 Map <String , Object > map = new HashMap <>();
440471 map .putAll (mapCursors .serialize ());
441- map .put ("pixels" , this .pixels . stream ().map ( MapPixel :: serialize ). collect ( Collectors . toList ( )));
472+ map .put ("pixels" , this .layers . entrySet (). stream ().collect ( Collectors . toMap ( Map . Entry :: getKey , mapPixels -> mapPixels . getValue (). stream (). map ( MapPixel :: serialize ) )));
442473 return map ;
443474 }
444475
@@ -463,13 +494,22 @@ public static MapRendererData deserialize(Map<String, Object> map) {
463494 if (pixels instanceof List <?>) {
464495 for (Object pixel : (List <?>) pixels ) {
465496 Map <String , Object > pixelMap = (Map <String , Object >) pixel ;
466- String type = (String ) pixelMap .get ("type" );
467- if (type .equals ("MapColoredPixel" ))
468- mapRendererData .addPixel (MapColoredPixel .deserialize (pixelMap ));
469- if (type .equals ("TextOverlay" ))
470- mapRendererData .addText (TextOverlay .deserialize (pixelMap ));
471- if (type .equals ("ImageOverlay" ))
472- mapRendererData .addImage (ImageOverlay .deserialize (pixelMap ));
497+ for (Map .Entry <String , Object > mapPixels : pixelMap .entrySet ()) {
498+ Map <String , Object > mapPixelsValue = (Map <String , Object >) mapPixels .getValue ();
499+ String type = (String ) mapPixelsValue .get ("type" );
500+ int layer ;
501+ try {
502+ layer = Integer .parseInt (mapPixels .getKey ());
503+ } catch (NumberFormatException ignore ) {
504+ layer = 0 ;
505+ }
506+ if (type .equals ("MapColoredPixel" ))
507+ mapRendererData .addPixel (layer , MapColoredPixel .deserialize (mapPixelsValue ));
508+ if (type .equals ("TextOverlay" ))
509+ mapRendererData .addText (layer , TextOverlay .deserialize (mapPixelsValue ));
510+ if (type .equals ("ImageOverlay" ))
511+ mapRendererData .addImage (layer , ImageOverlay .deserialize (mapPixelsValue ));
512+ }
473513 }
474514 }
475515 return mapRendererData ;
@@ -486,6 +526,10 @@ public String toString() {
486526 '}' ;
487527 }
488528
529+ private void addMapPixel (final int layer , final MapPixel mapPixel ) {
530+ layers .computeIfAbsent (layer , k -> new ArrayList <>()).add (mapPixel );
531+ }
532+
489533 private void setPixels (@ Nonnull final MapCanvas canvas ) {
490534 getPixels ().forEach (mapPixel -> mapPixel .render (this , canvas ));
491535 }
0 commit comments