Color class

Пакет: com.hypixel.hytale.server.core.universe.world.worldmap.provider.chunk

Файл: com/hypixel/hytale/server/core/universe/world/worldmap/provider/chunk/ImageBuilder.java

Поля (4)

МодификаторыТипИмя
public int a
public int b
public int g
public int r

Методы (2)

МодификаторыВозвратСигнатура
public void multiplypublic void multiply(float var1)
public int packpublic int pack()

Исходный код

Показать/скрыть
class="kw">package com.hypixel.hytale.server.core.universe.world.worldmap.provider.chunk;

class="kw">import com.hypixel.hytale.component.Ref;
class="kw">import com.hypixel.hytale.component.Store;
class="kw">import com.hypixel.hytale.math.util.ChunkUtil;
class="kw">import com.hypixel.hytale.protocol.ShaderType;
class="kw">import com.hypixel.hytale.protocol.packets.worldmap.MapImage;
class="kw">import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
class="kw">import com.hypixel.hytale.server.core.asset.type.environment.config.Environment;
class="kw">import com.hypixel.hytale.server.core.asset.type.fluid.Fluid;
class="kw">import com.hypixel.hytale.server.core.universe.world.World;
class="kw">import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
class="kw">import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn;
class="kw">import com.hypixel.hytale.server.core.universe.world.chunk.palette.BitFieldArr;
class="kw">import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection;
class="kw">import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
class="kw">import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
class="kw">import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
class="kw">import java.util.Arrays;
class="kw">import java.util.concurrent.CompletableFuture;
class="kw">import java.util.concurrent.ConcurrentLinkedQueue;
class="kw">import java.util.concurrent.atomic.AtomicInteger;
class="kw">import javax.annotation.Nonnull;
class="kw">import javax.annotation.Nullable;

class="kw">public class ImageBuilder {
   class="kw">private class="kw">static class="kw">final int POOL_MAX_SIZE = 64;
   class="kw">private class="kw">static class="kw">final AtomicInteger POOL_SIZE = new AtomicInteger(0);
   class="kw">private class="kw">static class="kw">final ConcurrentLinkedQueue<ImageBuilder> POOL = new ConcurrentLinkedQueue<>();
   class="kw">private class="kw">static class="kw">volatile boolean quantizationEnabled = true;
   class="kw">private class="kw">static class="kw">final float LX = -0.2F;
   class="kw">private class="kw">static class="kw">final float LY = 0.8F;
   class="kw">private class="kw">static class="kw">final float LZ = 0.5F;
   class="kw">private class="kw">static class="kw">final float INV_L = 1.0F / (float)Math.sqrt(0.93000007F);
   class="kw">private class="kw">static class="kw">final float LIGHT_X = -0.2F * INV_L;
   class="kw">private class="kw">static class="kw">final float LIGHT_Y = 0.8F * INV_L;
   class="kw">private class="kw">static class="kw">final float LIGHT_Z = 0.5F * INV_L;
   class="kw">private long index;
   class="kw">private World world;
   class="kw">private class="kw">final int imageWidth;
   class="kw">private class="kw">final int imageHeight;
   @Nonnull
   class="kw">private class="kw">final int[] rawPixels;
   @Nullable
   class="kw">private MapImage image;
   class="kw">private class="kw">final int sampleWidth;
   class="kw">private class="kw">final int sampleHeight;
   class="kw">private class="kw">final int blockStepX;
   class="kw">private class="kw">final int blockStepZ;
   @Nonnull
   class="kw">private class="kw">final short[] heightSamples;
   @Nonnull
   class="kw">private class="kw">final int[] tintSamples;
   @Nonnull
   class="kw">private class="kw">final int[] blockSamples;
   @Nonnull
   class="kw">private class="kw">final short[] neighborHeightSamples;
   @Nonnull
   class="kw">private class="kw">final short[][] fluidDepthSamples;
   @Nonnull
   class="kw">private class="kw">final int[] environmentSamples;
   @Nonnull
   class="kw">private class="kw">final int[][] fluidSamples;
   @Nonnull
   class="kw">private class="kw">final int[] fluidIdBuffer;
   @Nonnull
   class="kw">private class="kw">final short[] fluidDepthBuffer;
   @Nonnull
   class="kw">private class="kw">final int[] processedPixels;
   @Nonnull
   class="kw">private class="kw">final int[] quantizedPixels;
   @Nonnull
   class="kw">private class="kw">final FluidSection[] fluidSections;
   @Nonnull
   class="kw">private class="kw">final IntOpenHashSet uniqueColors;
   @Nonnull
   class="kw">private class="kw">final Int2IntOpenHashMap colorToIndex;
   class="kw">private class="kw">final ImageBuilder.Color outColor = new ImageBuilder.Color();
   @Nullable
   class="kw">private BlockChunk blockChunk;
   class="kw">private class="kw">static class="kw">final int QUANTIZE_STEP = 8;
   class="kw">private class="kw">static class="kw">final int QUANTIZE_HALF = 4;
   class="kw">private class="kw">static class="kw">final int[][] BAYER_MATRIX = new int[][]{{0, 8, 2, 10}, {12, 4, 14, 6}, {3, 11, 1, 9}, {15, 7, 13, 5}};
   class="kw">private class="kw">static class="kw">final int GRADIENT_THRESHOLD = 2;

   class="kw">public class="kw">static boolean isQuantizationEnabled() {
      class="kw">return quantizationEnabled;
   }

   class="kw">public class="kw">static boolean toggleQuantization() {
      quantizationEnabled = !quantizationEnabled;
      class="kw">return quantizationEnabled;
   }

   class="kw">private void reset(long var1, World var3) {
      this.index = var1;
      this.world = var3;
      this.image = null;
      Arrays.fill(this.fluidSamples, null);
      Arrays.fill(this.fluidDepthSamples, null);
      Arrays.fill(this.fluidSections, null);
      Arrays.fill(this.neighborHeightSamples, (short)0);
      this.uniqueColors.clear();
      this.colorToIndex.clear();
   }

   class="kw">public ImageBuilder(long var1, int var3, int var4, World var5) {
      this.index = var1;
      this.world = var5;
      this.imageWidth = var3;
      this.imageHeight = var4;
      this.rawPixels = new int[var3 * var4];
      this.sampleWidth = Math.min(32, var3);
      this.sampleHeight = Math.min(32, var4);
      this.blockStepX = Math.max(1, 32 / var3);
      this.blockStepZ = Math.max(1, 32 / var4);
      this.heightSamples = new short[this.sampleWidth * this.sampleHeight];
      this.tintSamples = new int[this.sampleWidth * this.sampleHeight];
      this.blockSamples = new int[this.sampleWidth * this.sampleHeight];
      this.neighborHeightSamples = new short[(this.sampleWidth + 2) * (this.sampleHeight + 2)];
      this.fluidDepthSamples = new short[this.sampleWidth * this.sampleHeight][];
      this.environmentSamples = new int[this.sampleWidth * this.sampleHeight];
      this.fluidSamples = new int[this.sampleWidth * this.sampleHeight][];
      this.fluidIdBuffer = new int[320];
      this.fluidDepthBuffer = new short[320];
      this.fluidSections = new FluidSection[10];
      this.processedPixels = new int[var3 * var4];
      this.uniqueColors = new IntOpenHashSet();
      this.colorToIndex = new Int2IntOpenHashMap();
      this.quantizedPixels = new int[var3 * var4];
   }

   class="kw">public long getIndex() {
      class="kw">return this.index;
   }

   @Nonnull
   class="kw">public MapImage getImage() {
      class="kw">return this.image;
   }

   @Nonnull
   class="kw">private CompletableFuture<ImageBuilder> fetchChunk() {
      class="kw">return this.world.getChunkStore().getChunkReferenceAsync(this.index).thenApplyAsync(var1 -> {
         if (var1 != null && var1.isValid()) {
            Store var2 = var1.getStore();
            this.blockChunk = var2.getComponent((Ref<ChunkStore>)var1, BlockChunk.getComponentType());
            ChunkColumn var3 = var2.getComponent((Ref<ChunkStore>)var1, ChunkColumn.getComponentType());

            for (int var4 = 0; var4 < 10; var4++) {
               Ref var5 = var3.getSection(var4);
               this.fluidSections[var4] = this.world.getChunkStore().getStore().getComponent(var5, FluidSection.getComponentType());
            }

            class="kw">return this;
         } else {
            class="kw">return null;
         }
      }, this.world);
   }

   @Nonnull
   class="kw">private CompletableFuture<ImageBuilder> sampleNeighborsSync() {
      CompletableFuture var1 = this.world
         .getChunkStore()
         .getChunkReferenceAsync(ChunkUtil.indexChunk(this.blockChunk.getX(), this.blockChunk.getZ() - 1))
         .thenAcceptAsync(var1x -> {
            if (var1x != null && var1x.isValid()) {
               BlockChunk var2 = var1x.getStore().getComponent((Ref<ChunkStore>)var1x, BlockChunk.getComponentType());
               int var3 = (this.sampleHeight - 1) * this.blockStepZ;

               for (int var4 = 0; var4 < this.sampleWidth; var4++) {
                  int var5 = var4 * this.blockStepX;
                  this.neighborHeightSamples[1 + var4] = var2.getHeight(var5, var3);
               }
            }
         }, this.world);
      CompletableFuture var2 = this.world
         .getChunkStore()
         .getChunkReferenceAsync(ChunkUtil.indexChunk(this.blockChunk.getX(), this.blockChunk.getZ() + 1))
         .thenAcceptAsync(var1x -> {
            if (var1x != null && var1x.isValid()) {
               BlockChunk var2 = var1x.getStore().getComponent((Ref<ChunkStore>)var1x, BlockChunk.getComponentType());
               int var3 = 0;
               int var4 = (this.sampleHeight + 1) * (this.sampleWidth + 2) + 1;

               for (int var5 = 0; var5 < this.sampleWidth; var5++) {
                  int var6 = var5 * this.blockStepX;
                  this.neighborHeightSamples[var4 + var5] = var2.getHeight(var6, var3);
               }
            }
         }, this.world);
      CompletableFuture var3 = this.world
         .getChunkStore()
         .getChunkReferenceAsync(ChunkUtil.indexChunk(this.blockChunk.getX() - 1, this.blockChunk.getZ()))
         .thenAcceptAsync(var1x -> {
            if (var1x != null && var1x.isValid()) {
               BlockChunk var2 = var1x.getStore().getComponent((Ref<ChunkStore>)var1x, BlockChunk.getComponentType());
               int var3 = (this.sampleWidth - 1) * this.blockStepX;

               for (int var4 = 0; var4 < this.sampleHeight; var4++) {
                  int var5 = var4 * this.blockStepZ;
                  this.neighborHeightSamples[(var4 + 1) * (this.sampleWidth + 2)] = var2.getHeight(var3, var5);
               }
            }
         }, this.world);
      CompletableFuture var4 = this.world
         .getChunkStore()
         .getChunkReferenceAsync(ChunkUtil.indexChunk(this.blockChunk.getX() + 1, this.blockChunk.getZ()))
         .thenAcceptAsync(var1x -> {
            if (var1x != null && var1x.isValid()) {
               BlockChunk var2 = var1x.getStore().getComponent((Ref<ChunkStore>)var1x, BlockChunk.getComponentType());
               int var3 = 0;

               for (int var4 = 0; var4 < this.sampleHeight; var4++) {
                  int var5 = var4 * this.blockStepZ;
                  this.neighborHeightSamples[(var4 + 1) * (this.sampleWidth + 2) + this.sampleWidth + 1] = var2.getHeight(var3, var5);
               }
            }
         }, this.world);
      CompletableFuture var5 = this.world
         .getChunkStore()
         .getChunkReferenceAsync(ChunkUtil.indexChunk(this.blockChunk.getX() + 1, this.blockChunk.getZ() - 1))
         .thenAcceptAsync(var1x -> {
            if (var1x != null && var1x.isValid()) {
               BlockChunk var2 = var1x.getStore().getComponent((Ref<ChunkStore>)var1x, BlockChunk.getComponentType());
               int var3 = 0;
               int var4 = (this.sampleHeight - 1) * this.blockStepZ;
               this.neighborHeightSamples[this.sampleWidth + 1] = var2.getHeight(var3, var4);
            }
         }, this.world);
      CompletableFuture var6 = this.world
         .getChunkStore()
         .getChunkReferenceAsync(ChunkUtil.indexChunk(this.blockChunk.getX() - 1, this.blockChunk.getZ() - 1))
         .thenAcceptAsync(var1x -> {
            if (var1x != null && var1x.isValid()) {
               BlockChunk var2 = var1x.getStore().getComponent((Ref<ChunkStore>)var1x, BlockChunk.getComponentType());
               int var3 = (this.sampleWidth - 1) * this.blockStepX;
               int var4 = (this.sampleHeight - 1) * this.blockStepZ;
               this.neighborHeightSamples[0] = var2.getHeight(var3, var4);
            }
         }, this.world);
      CompletableFuture var7 = this.world
         .getChunkStore()
         .getChunkReferenceAsync(ChunkUtil.indexChunk(this.blockChunk.getX() + 1, this.blockChunk.getZ() + 1))
         .thenAcceptAsync(var1x -> {
            if (var1x != null && var1x.isValid()) {
               BlockChunk var2 = var1x.getStore().getComponent((Ref<ChunkStore>)var1x, BlockChunk.getComponentType());
               int var3 = 0;
               int var4 = 0;
               this.neighborHeightSamples[(this.sampleHeight + 1) * (this.sampleWidth + 2) + this.sampleWidth + 1] = var2.getHeight(var3, var4);
            }
         }, this.world);
      CompletableFuture var8 = this.world
         .getChunkStore()
         .getChunkReferenceAsync(ChunkUtil.indexChunk(this.blockChunk.getX() - 1, this.blockChunk.getZ() + 1))
         .thenAcceptAsync(var1x -> {
            if (var1x != null && var1x.isValid()) {
               BlockChunk var2 = var1x.getStore().getComponent((Ref<ChunkStore>)var1x, BlockChunk.getComponentType());
               int var3 = (this.sampleWidth - 1) * this.blockStepX;
               int var4 = 0;
               this.neighborHeightSamples[(this.sampleHeight + 1) * (this.sampleWidth + 2)] = var2.getHeight(var3, var4);
            }
         }, this.world);
      class="kw">return CompletableFuture.allOf(var1, var2, var3, var4, var5, var6, var7, var8).thenApply(var1x -> this);
   }

   @Nonnull
   class="kw">private ImageBuilder generateImageAsync() {
      for (int var1 = 0; var1 < this.sampleHeight; var1++) {
         for (int var2 = 0; var2 < this.sampleWidth; var2++) {
            int var3 = var1 * this.sampleWidth + var2;
            int var4 = var2 * this.blockStepX;
            int var5 = var1 * this.blockStepZ;
            short var6 = this.blockChunk.getHeight(var4, var5);
            int var7 = this.blockChunk.getTint(var4, var5);
            this.heightSamples[var3] = var6;
            this.tintSamples[var3] = var7;
            int var8 = this.blockChunk.getBlock(var4, var6, var5);
            this.blockSamples[var3] = var8;
            int var9 = 0;
            int var10 = 320;
            Fluid var11 = null;
            int var12 = 0;
            int var13 = 0;
            int var14 = ChunkUtil.chunkCoordinate(var6);
            int var15 = 9;

            label160:
            while (var15 >= 0 && var15 >= var14) {
               FluidSection var16 = this.fluidSections[var15];
               if (var16 != null && !var16.isEmpty()) {
                  int var17 = Math.max(ChunkUtil.minBlock(var15), var6);
                  int var18 = ChunkUtil.maxBlock(var15);

                  for (int var19 = var18; var19 >= var17; var19--) {
                     int var20 = var16.getFluidId(var4, var19, var5);
                     if (var20 != var9) {
                        if (var9 != 0) {
                           this.fluidIdBuffer[var13] = var9;
                           this.fluidDepthBuffer[var13] = (short)var12;
                           var13++;
                           if (var11 != null && !isWaterShaded(var11)) {
                              break label160;
                           }
                        }

                        var9 = var20;
                        var12 = 0;
                        if (var9 != 0) {
                           var11 = Fluid.getAssetMap().getAsset(var9);
                           if (var10 == 320) {
                              var10 = var19;
                           }
                        } else {
                           var11 = null;
                        }
                     }

                     if (var9 != 0) {
                        var12++;
                     }
                  }

                  var15--;
               } else {
                  if (var9 != 0) {
                     this.fluidIdBuffer[var13] = var9;
                     this.fluidDepthBuffer[var13] = (short)var12;
                     var13++;
                     if (var11 != null && !isWaterShaded(var11)) {
                        break;
                     }

                     var9 = 0;
                     var12 = 0;
                     var11 = null;
                  }

                  var15--;
               }
            }

            if (var9 != 0) {
               this.fluidIdBuffer[var13] = var9;
               this.fluidDepthBuffer[var13] = (short)var12;
               var13++;
            }

            if (var13 > 0) {
               this.fluidSamples[var3] = Arrays.copyOf(this.fluidIdBuffer, var13);
               this.fluidDepthSamples[var3] = Arrays.copyOf(this.fluidDepthBuffer, var13);
            }

            int var46 = var13 > 0 ? this.blockChunk.getEnvironment(var4, var10, var5) : 0;
            this.environmentSamples[var3] = var46;
         }
      }

      float var30 = (float)this.sampleWidth / this.imageWidth;
      float var31 = (float)this.sampleHeight / this.imageHeight;
      int var32 = Math.max(1, this.imageWidth / this.sampleWidth);
      int var33 = Math.max(1, this.imageHeight / this.sampleHeight);

      for (int var34 = 0; var34 < this.sampleHeight; var34++) {
         System.arraycopy(this.heightSamples, var34 * this.sampleWidth, this.neighborHeightSamples, (var34 + 1) * (this.sampleWidth + 2) + 1, this.sampleWidth);
      }

      for (int var35 = 0; var35 < this.imageHeight; var35++) {
         int var36 = Math.min((int)(var35 * var31), this.sampleHeight - 1);
         int var37 = var35 % var33;

         for (int var38 = 0; var38 < this.imageWidth; var38++) {
            int var39 = Math.min((int)(var38 * var30), this.sampleWidth - 1);
            int var40 = var38 % var32;
            int var41 = var36 * this.sampleWidth + var39;
            short var42 = this.heightSamples[var41];
            int var43 = this.tintSamples[var41];
            int var44 = this.blockSamples[var41];
            int[] var45 = this.fluidSamples[var41];
            boolean var47 = var45 != null && var45.length > 0;
            if ((var42 < 0 || var44 == 0) && !var47) {
               this.outColor.r = this.outColor.g = this.outColor.b = this.outColor.a = 0;
               this.packImageData(var38, var35);
            } else {
               if (var42 >= 0 && var44 != 0) {
                  getBlockColor(var44, var43, this.outColor);
               } else {
                  this.outColor.r = this.outColor.g = this.outColor.b = 0;
                  this.outColor.a = 255;
               }

               short var48 = this.neighborHeightSamples[var36 * (this.sampleWidth + 2) + var39 + 1];
               short var49 = this.neighborHeightSamples[(var36 + 2) * (this.sampleWidth + 2) + var39 + 1];
               short var50 = this.neighborHeightSamples[(var36 + 1) * (this.sampleWidth + 2) + var39];
               short var51 = this.neighborHeightSamples[(var36 + 1) * (this.sampleWidth + 2) + var39 + 2];
               short var21 = this.neighborHeightSamples[var36 * (this.sampleWidth + 2) + var39];
               short var22 = this.neighborHeightSamples[var36 * (this.sampleWidth + 2) + var39 + 2];
               short var23 = this.neighborHeightSamples[(var36 + 2) * (this.sampleWidth + 2) + var39];
               short var24 = this.neighborHeightSamples[(var36 + 2) * (this.sampleWidth + 2) + var39 + 2];
               float var25 = shadeFromHeights(var40, var37, var32, var33, var42, var48, var49, var50, var51, var21, var22, var23, var24);
               this.outColor.multiply(var25);
               if (var42 < 320 && var47) {
                  short[] var26 = this.fluidDepthSamples[var41];
                  int var27 = this.environmentSamples[var41];

                  for (int var28 = var45.length - 1; var28 >= 0; var28--) {
                     int var29 = var45[var28];
                     if (var29 != 0) {
                        getFluidColor(var29, var27, var26[var28], this.outColor);
                     }
                  }
               }

               this.packImageData(var38, var35);
            }
         }
      }

      this.image = this.encodeToPalette();
      class="kw">return this;
   }

   class="kw">private class="kw">static int quantizeChannel(int var0) {
      class="kw">return Math.min(255, (var0 + 4) / 8 * 8);
   }

   class="kw">private class="kw">static boolean isNearBoundary(int var0) {
      int var1 = (var0 + 4) % 8;
      class="kw">return var1 <= 2 || var1 >= 6;
   }

   class="kw">private class="kw">static int quantizeChannelWithDither(int var0, int var1) {
      int var2 = var0 + var1;
      var2 = Math.clamp(var2, 0, 255);
      class="kw">return Math.min(255, (var2 + 4) / 8 * 8);
   }

   class="kw">private class="kw">static int quantizeColor(int var0) {
      int var1 = quantizeChannel(var0 >> 24 & 0xFF);
      int var2 = quantizeChannel(var0 >> 16 & 0xFF);
      int var3 = quantizeChannel(var0 >> 8 & 0xFF);
      int var4 = var0 & 0xFF;
      class="kw">return var1 << 24 | var2 << 16 | var3 << 8 | var4;
   }

   class="kw">private class="kw">static boolean colorNearBoundary(int var0) {
      int var1 = var0 >> 24 & 0xFF;
      int var2 = var0 >> 16 & 0xFF;
      int var3 = var0 >> 8 & 0xFF;
      class="kw">return isNearBoundary(var1) || isNearBoundary(var2) || isNearBoundary(var3);
   }

   class="kw">private class="kw">static int quantizeColorWithDither(int var0, int var1, int var2) {
      int var3 = BAYER_MATRIX[var2 & 3][var1 & 3];
      int var4 = (var3 - 8) * 8 / 16;
      int var5 = quantizeChannelWithDither(var0 >> 24 & 0xFF, var4);
      int var6 = quantizeChannelWithDither(var0 >> 16 & 0xFF, var4);
      int var7 = quantizeChannelWithDither(var0 >> 8 & 0xFF, var4);
      int var8 = var0 & 0xFF;
      class="kw">return var5 << 24 | var6 << 16 | var7 << 8 | var8;
   }

   class="kw">private boolean isInTransitionZone(int var1) {
      int var2 = this.quantizedPixels[var1];
      int var3 = var1 % this.imageWidth;
      int var4 = var1 / this.imageWidth;

      for (int var5 = -2; var5 <= 2; var5++) {
         for (int var6 = -2; var6 <= 2; var6++) {
            if (var6 != 0 || var5 != 0) {
               int var7 = var3 + var6;
               int var8 = var4 + var5;
               if (var7 >= 0 && var7 < this.imageWidth && var8 >= 0 && var8 < this.imageHeight && this.quantizedPixels[var8 * this.imageWidth + var7] != var2) {
                  class="kw">return true;
               }
            }
         }
      }

      class="kw">return false;
   }

   @Nonnull
   class="kw">private MapImage encodeToPalette() {
      int var1 = this.rawPixels.length;
      if (quantizationEnabled) {
         for (int var2 = 0; var2 < var1; var2++) {
            this.quantizedPixels[var2] = quantizeColor(this.rawPixels[var2]);
         }
      }

      for (int var6 = 0; var6 < var1; var6++) {
         int var3;
         if (quantizationEnabled) {
            if (this.isInTransitionZone(var6)) {
               int var4 = var6 % this.imageWidth;
               int var5 = var6 / this.imageWidth;
               var3 = quantizeColorWithDither(this.rawPixels[var6], var4, var5);
            } else {
               var3 = this.quantizedPixels[var6];
            }
         } else {
            var3 = this.rawPixels[var6];
         }

         this.processedPixels[var6] = var3;
         this.uniqueColors.add(var3);
      }

      int[] var7 = this.uniqueColors.toIntArray();
      int var8 = calculateBitsRequired(var7.length);

      for (int var9 = 0; var9 < var7.length; var9++) {
         this.colorToIndex.put(var7[var9], var9);
      }

      BitFieldArr var10 = new BitFieldArr(var8, var1);

      for (int var11 = 0; var11 < var1; var11++) {
         var10.set(var11, this.colorToIndex.get(this.processedPixels[var11]));
      }

      byte[] var12 = var10.get();
      class="kw">return new MapImage(this.imageWidth, this.imageHeight, var7, (byte)var8, var12);
   }

   class="kw">private class="kw">static boolean isWaterShaded(Fluid var0) {
      class="kw">return var0.hasEffect(ShaderType.Water);
   }

   class="kw">private class="kw">static int calculateBitsRequired(int var0) {
      if (var0 <= 16) {
         class="kw">return 4;
      } else if (var0 <= 256) {
         class="kw">return 8;
      } else {
         class="kw">return var0 <= 4096 ? 12 : 16;
      }
   }

   class="kw">private void packImageData(int var1, int var2) {
      this.rawPixels[var2 * this.imageWidth + var1] = this.outColor.pack();
   }

   class="kw">private class="kw">static float shadeFromHeights(
      int var0, int var1, int var2, int var3, short var4, short var5, short var6, short var7, short var8, short var9, short var10, short var11, short var12
   ) {
      float var13 = (var0 + 0.5F) / var2;
      float var14 = (var1 + 0.5F) / var3;
      float var15 = (var13 + var14) / 2.0F;
      float var16 = (1.0F - var13 + var14) / 2.0F;
      float var17 = (var4 - var7) * (1.0F - var13) + (var8 - var4) * var13;
      float var18 = (var4 - var5) * (1.0F - var14) + (var6 - var4) * var14;
      float var19 = (var4 - var9) * (1.0F - var15) + (var12 - var4) * var15;
      float var20 = (var4 - var10) * (1.0F - var16) + (var11 - var4) * var16;
      float var21 = var17 * 2.0F + var19;
      float var22 = var18 * 2.0F + var20;
      float var23 = 3.0F;
      float var24 = var21;
      float var25 = var23;
      float var26 = var22;
      float var27 = 1.0F / (float)Math.sqrt(var24 * var24 + var25 * var25 + var26 * var26);
      var24 *= var27;
      var25 *= var27;
      var26 *= var27;
      float var28 = Math.max(0.0F, var24 * LIGHT_X + var25 * LIGHT_Y + var26 * LIGHT_Z);
      float var29 = 0.4F;
      float var30 = 0.6F;
      class="kw">return var29 + var30 * var28;
   }

   class="kw">private class="kw">static void getBlockColor(int var0, int var1, @Nonnull ImageBuilder.Color var2) {
      BlockType var3 = BlockType.getAssetMap().getAsset(var0);
      int var4 = var1 >> 16 & 0xFF;
      int var5 = var1 >> 8 & 0xFF;
      int var6 = var1 >> 0 & 0xFF;
      com.hypixel.hytale.protocol.Color[] var7 = var3.getTintUp();
      boolean var8 = var7 != null && var7.length > 0;
      int var9 = var8 ? var7[0].red & 0xFF : 255;
      int var10 = var8 ? var7[0].green & 0xFF : 255;
      int var11 = var8 ? var7[0].blue & 0xFF : 255;
      float var12 = var3.getBiomeTintUp() / 100.0F;
      int var13 = (int)(var9 + (var4 - var9) * var12);
      int var14 = (int)(var10 + (var5 - var10) * var12);
      int var15 = (int)(var11 + (var6 - var11) * var12);
      com.hypixel.hytale.protocol.Color var16 = var3.getParticleColor();
      if (var16 != null && var12 < 1.0F) {
         var13 = var13 * (var16.red & 255) / 255;
         var14 = var14 * (var16.green & 255) / 255;
         var15 = var15 * (var16.blue & 255) / 255;
      }

      var2.r = var13 & 0xFF;
      var2.g = var14 & 0xFF;
      var2.b = var15 & 0xFF;
      var2.a = 255;
   }

   class="kw">private class="kw">static void getFluidColor(int var0, int var1, int var2, @Nonnull ImageBuilder.Color var3) {
      int var4 = 255;
      int var5 = 255;
      int var6 = 255;
      Fluid var7 = Fluid.getAssetMap().getAsset(var0);
      if (var7 != null) {
         boolean var8 = isWaterShaded(var7);
         if (var8) {
            Environment var9 = Environment.getAssetMap().getAsset(var1);
            com.hypixel.hytale.protocol.Color var10 = var9.getWaterTint();
            if (var10 != null) {
               var4 = var4 * (var10.red & 255) / 255;
               var5 = var5 * (var10.green & 255) / 255;
               var6 = var6 * (var10.blue & 255) / 255;
            }
         }

         com.hypixel.hytale.protocol.Color var11 = var7.getParticleColor();
         if (var11 != null) {
            var4 = var4 * (var11.red & 255) / 255;
            var5 = var5 * (var11.green & 255) / 255;
            var6 = var6 * (var11.blue & 255) / 255;
         }

         if (var8) {
            float var12 = 1.0F / (var2 + 1.0F);
            var3.r = (int)(var4 + ((var3.r & 0xFF) - var4) * var12) & 0xFF;
            var3.g = (int)(var5 + ((var3.g & 0xFF) - var5) * var12) & 0xFF;
            var3.b = (int)(var6 + ((var3.b & 0xFF) - var6) * var12) & 0xFF;
         } else {
            float var13 = Math.max(0.75F, 1.0F - var2 * 0.02F);
            var3.r = (int)((var4 & 0xFF) * var13);
            var3.g = (int)((var5 & 0xFF) * var13);
            var3.b = (int)((var6 & 0xFF) * var13);
         }
      }
   }

   class="kw">public void release() {
      if (POOL_SIZE.incrementAndGet() <= 64) {
         this.blockChunk = null;
         this.image = null;
         Arrays.fill(this.fluidSections, null);
         POOL.offer(this);
      } else {
         POOL_SIZE.decrementAndGet();
      }
   }

   class="kw">private class="kw">static ImageBuilder acquire(long var0, int var2, int var3, World var4) {
      ImageBuilder var5 = POOL.poll();
      if (var5 != null) {
         POOL_SIZE.decrementAndGet();
         if (var5.imageWidth == var2 && var5.imageHeight == var3) {
            var5.reset(var0, var4);
            class="kw">return var5;
         }
      }

      class="kw">return new ImageBuilder(var0, var2, var3, var4);
   }

   @Nonnull
   class="kw">public class="kw">static CompletableFuture<ImageBuilder> build(long var0, int var2, int var3, World var4) {
      class="kw">return CompletableFuture.completedFuture(acquire(var0, var2, var3, var4))
         .thenCompose(ImageBuilder::fetchChunk)
         .thenCompose(var0x -> var0x != null ? var0x.sampleNeighborsSync() : CompletableFuture.completedFuture(null))
         .thenApplyAsync(var0x -> var0x != null ? var0x.generateImageAsync() : null);
   }

   class="kw">private class="kw">static class Color {
      class="kw">public int r;
      class="kw">public int g;
      class="kw">public int b;
      class="kw">public int a;

      class="kw">private Color() {
      }

      class="kw">public int pack() {
         class="kw">return (this.r & 0xFF) << 24 | (this.g & 0xFF) << 16 | (this.b & 0xFF) << 8 | this.a & 0xFF;
      }

      class="kw">public void multiply(float var1) {
         this.r = Math.clamp((int)(this.r * var1), 0, 255);
         this.g = Math.clamp((int)(this.g * var1), 0, 255);
         this.b = Math.clamp((int)(this.b * var1), 0, 255);
      }
   }
}