minecraft-src/net/minecraft/client/gui/GuiGraphics.java
2025-07-04 02:00:41 +03:00

1282 lines
47 KiB
Java

package net.minecraft.client.gui;
import com.mojang.blaze3d.platform.Lighting;
import com.mojang.blaze3d.platform.Window;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.CrashReportDetail;
import net.minecraft.ReportedException;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.navigation.ScreenRectangle;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipPositioner;
import net.minecraft.client.gui.screens.inventory.tooltip.DefaultTooltipPositioner;
import net.minecraft.client.gui.screens.inventory.tooltip.TooltipRenderUtil;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.metadata.gui.GuiSpriteScaling;
import net.minecraft.client.resources.metadata.gui.GuiSpriteScaling.NineSlice.Border;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.FormattedText;
import net.minecraft.network.chat.HoverEvent;
import net.minecraft.network.chat.Style;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.ItemTags;
import net.minecraft.util.ARGB;
import net.minecraft.util.FormattedCharSequence;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.inventory.tooltip.TooltipComponent;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.joml.Vector2ic;
@Environment(EnvType.CLIENT)
public class GuiGraphics {
public static final float MAX_GUI_Z = 10000.0F;
public static final float MIN_GUI_Z = -10000.0F;
private static final int EXTRA_SPACE_AFTER_FIRST_TOOLTIP_LINE = 2;
private final Minecraft minecraft;
private final PoseStack pose;
private final MultiBufferSource.BufferSource bufferSource;
private final GuiGraphics.ScissorStack scissorStack = new GuiGraphics.ScissorStack();
private final GuiSpriteManager sprites;
private GuiGraphics(Minecraft minecraft, PoseStack pose, MultiBufferSource.BufferSource bufferSource) {
this.minecraft = minecraft;
this.pose = pose;
this.bufferSource = bufferSource;
this.sprites = minecraft.getGuiSprites();
}
public GuiGraphics(Minecraft minecraft, MultiBufferSource.BufferSource bufferSource) {
this(minecraft, new PoseStack(), bufferSource);
}
/**
* {@return returns the width of the GUI screen in pixels}
*/
public int guiWidth() {
return this.minecraft.getWindow().getGuiScaledWidth();
}
/**
* {@return returns the height of the GUI screen in pixels}
*/
public int guiHeight() {
return this.minecraft.getWindow().getGuiScaledHeight();
}
/**
* {@return returns the PoseStack used for transformations and rendering.}
*/
public PoseStack pose() {
return this.pose;
}
/**
* Flushes the render state, ending the current batch and enabling depth testing.
*/
public void flush() {
this.bufferSource.endBatch();
}
/**
* Draws a horizontal line from minX to maxX at the specified y-coordinate with the given color.
*
* @param minX the x-coordinate of the start point.
* @param maxX the x-coordinate of the end point.
* @param y the y-coordinate of the line.
* @param color the color of the line.
*/
public void hLine(int minX, int maxX, int y, int color) {
this.hLine(RenderType.gui(), minX, maxX, y, color);
}
/**
* Draws a horizontal line from minX to maxX at the specified y-coordinate with the given color using the specified render type.
*
* @param renderType the render type to use.
* @param minX the x-coordinate of the start point.
* @param maxX the x-coordinate of the end point.
* @param y the y-coordinate of the line.
* @param color the color of the line.
*/
public void hLine(RenderType renderType, int minX, int maxX, int y, int color) {
if (maxX < minX) {
int i = minX;
minX = maxX;
maxX = i;
}
this.fill(renderType, minX, y, maxX + 1, y + 1, color);
}
/**
* Draws a vertical line from minY to maxY at the specified x-coordinate with the given color.
*
* @param x the x-coordinate of the line.
* @param minY the y-coordinate of the start point.
* @param maxY the y-coordinate of the end point.
* @param color the color of the line.
*/
public void vLine(int x, int minY, int maxY, int color) {
this.vLine(RenderType.gui(), x, minY, maxY, color);
}
/**
* Draws a vertical line from minY to maxY at the specified x-coordinate with the given color using the specified render type.
*
* @param renderType the render type to use.
* @param x the x-coordinate of the line.
* @param minY the y-coordinate of the start point.
* @param maxY the y-coordinate of the end point.
* @param color the color of the line.
*/
public void vLine(RenderType renderType, int x, int minY, int maxY, int color) {
if (maxY < minY) {
int i = minY;
minY = maxY;
maxY = i;
}
this.fill(renderType, x, minY + 1, x + 1, maxY, color);
}
/**
* Enables scissoring with the specified screen coordinates.
*
* @param minX the minimum x-coordinate of the scissor region.
* @param minY the minimum y-coordinate of the scissor region.
* @param maxX the maximum x-coordinate of the scissor region.
* @param maxY the maximum y-coordinate of the scissor region.
*/
public void enableScissor(int minX, int minY, int maxX, int maxY) {
this.applyScissor(this.scissorStack.push(new ScreenRectangle(minX, minY, maxX - minX, maxY - minY)));
}
/**
* Disables scissoring.
*/
public void disableScissor() {
this.applyScissor(this.scissorStack.pop());
}
public boolean containsPointInScissor(int x, int y) {
return this.scissorStack.containsPoint(x, y);
}
/**
* Applies scissoring based on the provided screen rectangle.
*
* @param rectangle the screen rectangle to apply scissoring with. Can be null to disable scissoring.
*/
private void applyScissor(@Nullable ScreenRectangle rectangle) {
this.flush();
if (rectangle != null) {
Window window = Minecraft.getInstance().getWindow();
int i = window.getHeight();
double d = window.getGuiScale();
double e = rectangle.left() * d;
double f = i - rectangle.bottom() * d;
double g = rectangle.width() * d;
double h = rectangle.height() * d;
RenderSystem.enableScissor((int)e, (int)f, Math.max(0, (int)g), Math.max(0, (int)h));
} else {
RenderSystem.disableScissor();
}
}
/**
* Fills a rectangle with the specified color using the given coordinates as the boundaries.
*
* @param minX the minimum x-coordinate of the rectangle.
* @param minY the minimum y-coordinate of the rectangle.
* @param maxX the maximum x-coordinate of the rectangle.
* @param maxY the maximum y-coordinate of the rectangle.
* @param color the color to fill the rectangle with.
*/
public void fill(int minX, int minY, int maxX, int maxY, int color) {
this.fill(minX, minY, maxX, maxY, 0, color);
}
/**
* Fills a rectangle with the specified color and z-level using the given coordinates as the boundaries.
*
* @param minX the minimum x-coordinate of the rectangle.
* @param minY the minimum y-coordinate of the rectangle.
* @param maxX the maximum x-coordinate of the rectangle.
* @param maxY the maximum y-coordinate of the rectangle.
* @param z the z-level of the rectangle.
* @param color the color to fill the rectangle with.
*/
public void fill(int minX, int minY, int maxX, int maxY, int z, int color) {
this.fill(RenderType.gui(), minX, minY, maxX, maxY, z, color);
}
/**
* Fills a rectangle with the specified color using the given render type and coordinates as the boundaries.
*
* @param renderType the render type to use.
* @param minX the minimum x-coordinate of the rectangle.
* @param minY the minimum y-coordinate of the rectangle.
* @param maxX the maximum x-coordinate of the rectangle.
* @param maxY the maximum y-coordinate of the rectangle.
* @param color the color to fill the rectangle with.
*/
public void fill(RenderType renderType, int minX, int minY, int maxX, int maxY, int color) {
this.fill(renderType, minX, minY, maxX, maxY, 0, color);
}
/**
* Fills a rectangle with the specified color and z-level using the given render type and coordinates as the boundaries.
*
* @param renderType the render type to use.
* @param minX the minimum x-coordinate of the rectangle.
* @param minY the minimum y-coordinate of the rectangle.
* @param maxX the maximum x-coordinate of the rectangle.
* @param maxY the maximum y-coordinate of the rectangle.
* @param z the z-level of the rectangle.
* @param color the color to fill the rectangle with.
*/
public void fill(RenderType renderType, int minX, int minY, int maxX, int maxY, int z, int color) {
Matrix4f matrix4f = this.pose.last().pose();
if (minX < maxX) {
int i = minX;
minX = maxX;
maxX = i;
}
if (minY < maxY) {
int i = minY;
minY = maxY;
maxY = i;
}
VertexConsumer vertexConsumer = this.bufferSource.getBuffer(renderType);
vertexConsumer.addVertex(matrix4f, (float)minX, (float)minY, (float)z).setColor(color);
vertexConsumer.addVertex(matrix4f, (float)minX, (float)maxY, (float)z).setColor(color);
vertexConsumer.addVertex(matrix4f, (float)maxX, (float)maxY, (float)z).setColor(color);
vertexConsumer.addVertex(matrix4f, (float)maxX, (float)minY, (float)z).setColor(color);
}
/**
* Fills a rectangle with a gradient color from colorFrom to colorTo using the given coordinates as the boundaries.
*
* @param x1 the x-coordinate of the first corner of the rectangle.
* @param y1 the y-coordinate of the first corner of the rectangle.
* @param x2 the x-coordinate of the second corner of the rectangle.
* @param y2 the y-coordinate of the second corner of the rectangle.
* @param colorFrom the starting color of the gradient.
* @param colorTo the ending color of the gradient.
*/
public void fillGradient(int x1, int y1, int x2, int y2, int colorFrom, int colorTo) {
this.fillGradient(x1, y1, x2, y2, 0, colorFrom, colorTo);
}
/**
* Fills a rectangle with a gradient color from colorFrom to colorTo at the specified z-level using the given coordinates as the boundaries.
*
* @param x1 the x-coordinate of the first corner of the rectangle.
* @param y1 the y-coordinate of the first corner of the rectangle.
* @param x2 the x-coordinate of the second corner of the rectangle.
* @param y2 the y-coordinate of the second corner of the rectangle.
* @param z the z-level of the rectangle.
* @param colorFrom the starting color of the gradient.
* @param colorTo the ending color of the gradient.
*/
public void fillGradient(int x1, int y1, int x2, int y2, int z, int colorFrom, int colorTo) {
this.fillGradient(RenderType.gui(), x1, y1, x2, y2, colorFrom, colorTo, z);
}
/**
* Fills a rectangle with a gradient color from colorFrom to colorTo at the specified z-level using the given render type and coordinates as the boundaries.
*
* @param renderType the render type to use.
* @param x1 the x-coordinate of the first corner of the rectangle.
* @param y1 the y-coordinate of the first corner of the rectangle.
* @param x2 the x-coordinate of the second corner of the rectangle.
* @param y2 the y-coordinate of the second corner of the rectangle.
* @param colorFrom the starting color of the gradient.
* @param colorTo the ending color of the gradient.
* @param z the z-level of the rectangle.
*/
public void fillGradient(RenderType renderType, int x1, int y1, int x2, int y2, int colorFrom, int colorTo, int z) {
VertexConsumer vertexConsumer = this.bufferSource.getBuffer(renderType);
this.fillGradient(vertexConsumer, x1, y1, x2, y2, z, colorFrom, colorTo);
}
/**
* The core `fillGradient` method.
* <p>
* Fills a rectangle with a gradient color from colorFrom to colorTo at the specified z-level using the given render type and coordinates as the boundaries.
*
* @param consumer the {@linkplain VertexConsumer} object for drawing the vertices on screen.
* @param x1 the x-coordinate of the first corner of the rectangle.
* @param y1 the y-coordinate of the first corner of the rectangle.
* @param x2 the x-coordinate of the second corner of the rectangle.
* @param y2 the y-coordinate of the second corner of the rectangle.
* @param z the z-level of the rectangle.
* @param colorFrom the starting color of the gradient.
* @param colorTo the ending color of the gradient.
*/
private void fillGradient(VertexConsumer consumer, int x1, int y1, int x2, int y2, int z, int colorFrom, int colorTo) {
Matrix4f matrix4f = this.pose.last().pose();
consumer.addVertex(matrix4f, (float)x1, (float)y1, (float)z).setColor(colorFrom);
consumer.addVertex(matrix4f, (float)x1, (float)y2, (float)z).setColor(colorTo);
consumer.addVertex(matrix4f, (float)x2, (float)y2, (float)z).setColor(colorTo);
consumer.addVertex(matrix4f, (float)x2, (float)y1, (float)z).setColor(colorFrom);
}
public void fillRenderType(RenderType renderType, int x1, int y1, int x2, int y2, int z) {
Matrix4f matrix4f = this.pose.last().pose();
VertexConsumer vertexConsumer = this.bufferSource.getBuffer(renderType);
vertexConsumer.addVertex(matrix4f, (float)x1, (float)y1, (float)z);
vertexConsumer.addVertex(matrix4f, (float)x1, (float)y2, (float)z);
vertexConsumer.addVertex(matrix4f, (float)x2, (float)y2, (float)z);
vertexConsumer.addVertex(matrix4f, (float)x2, (float)y1, (float)z);
}
/**
* Draws a centered string at the specified coordinates using the given font, text, and color.
*
* @param font the font to use for rendering.
* @param text the text to draw.
* @param x the x-coordinate of the center of the string.
* @param y the y-coordinate of the string.
* @param color the color of the string.
*/
public void drawCenteredString(Font font, String text, int x, int y, int color) {
this.drawString(font, text, x - font.width(text) / 2, y, color);
}
/**
* Draws a centered string at the specified coordinates using the given font, text component, and color.
*
* @param font the font to use for rendering.
* @param text the text component to draw.
* @param x the x-coordinate of the center of the string.
* @param y the y-coordinate of the string.
* @param color the color of the string.
*/
public void drawCenteredString(Font font, Component text, int x, int y, int color) {
FormattedCharSequence formattedCharSequence = text.getVisualOrderText();
this.drawString(font, formattedCharSequence, x - font.width(formattedCharSequence) / 2, y, color);
}
/**
* Draws a centered string at the specified coordinates using the given font, formatted character sequence, and color.
*
* @param font the font to use for rendering.
* @param text the formatted character sequence to draw.
* @param x the x-coordinate of the center of the string.
* @param y the y-coordinate of the string.
* @param color the color of the string.
*/
public void drawCenteredString(Font font, FormattedCharSequence text, int x, int y, int color) {
this.drawString(font, text, x - font.width(text) / 2, y, color);
}
/**
* Draws a string at the specified coordinates using the given font, text, and color. Returns the width of the drawn string.
* <p>
* @return the width of the drawn string.
*
* @param font the font to use for rendering.
* @param text the text to draw.
* @param x the x-coordinate of the string.
* @param y the y-coordinate of the string.
* @param color the color of the string.
*/
public int drawString(Font font, @Nullable String text, int x, int y, int color) {
return this.drawString(font, text, x, y, color, true);
}
/**
* Draws a string at the specified coordinates using the given font, text, color, and drop shadow. Returns the width of the drawn string.
* <p>
* @return the width of the drawn string.
*
* @param font the font to use for rendering.
* @param text the text to draw.
* @param x the x-coordinate of the string.
* @param y the y-coordinate of the string.
* @param color the color of the string.
* @param dropShadow whether to apply a drop shadow to the string.
*/
public int drawString(Font font, @Nullable String text, int x, int y, int color, boolean dropShadow) {
return text == null
? 0
: font.drawInBatch(text, (float)x, (float)y, color, dropShadow, this.pose.last().pose(), this.bufferSource, Font.DisplayMode.NORMAL, 0, 15728880);
}
/**
* Draws a formatted character sequence at the specified coordinates using the given font, text, and color. Returns the width of the drawn string.
* <p>
* @return the width of the drawn string.
*
* @param font the font to use for rendering.
* @param text the formatted character sequence to draw.
* @param x the x-coordinate of the string.
* @param y the y-coordinate of the string.
* @param color the color of the string
*/
public int drawString(Font font, FormattedCharSequence text, int x, int y, int color) {
return this.drawString(font, text, x, y, color, true);
}
/**
* Draws a formatted character sequence at the specified coordinates using the given font, text, color, and drop shadow. Returns the width of the drawn string.
* <p>
* @return returns the width of the drawn string.
*
* @param font the font to use for rendering.
* @param text the formatted character sequence to draw.
* @param x the x-coordinate of the string.
* @param y the y-coordinate of the string.
* @param color the color of the string.
* @param dropShadow whether to apply a drop shadow to the string.
*/
public int drawString(Font font, FormattedCharSequence text, int x, int y, int color, boolean dropShadow) {
return font.drawInBatch(text, (float)x, (float)y, color, dropShadow, this.pose.last().pose(), this.bufferSource, Font.DisplayMode.NORMAL, 0, 15728880);
}
/**
* Draws a component's visual order text at the specified coordinates using the given font, text component, and color.
* <p>
* @return the width of the drawn string.
*
* @param font the font to use for rendering.
* @param text the text component to draw.
* @param x the x-coordinate of the string.
* @param y the y-coordinate of the string.
* @param color the color of the string.
*/
public int drawString(Font font, Component text, int x, int y, int color) {
return this.drawString(font, text, x, y, color, true);
}
/**
* Draws a component's visual order text at the specified coordinates using the given font, text component, color, and drop shadow.
* <p>
* @return the width of the drawn string.
*
* @param font the font to use for rendering.
* @param text the text component to draw.
* @param x the x-coordinate of the string.
* @param y the y-coordinate of the string.
* @param color the color of the string.
* @param dropShadow whether to apply a drop shadow to the string.
*/
public int drawString(Font font, Component text, int x, int y, int color, boolean dropShadow) {
return this.drawString(font, text.getVisualOrderText(), x, y, color, dropShadow);
}
/**
* Draws a formatted text with word wrapping at the specified coordinates using the given font, text, line width, and color.
*
* @param font the font to use for rendering.
* @param text the formatted text to draw.
* @param x the x-coordinate of the starting position.
* @param y the y-coordinate of the starting position.
* @param lineWidth the maximum width of each line before wrapping.
* @param color the color of the text.
*/
public void drawWordWrap(Font font, FormattedText text, int x, int y, int lineWidth, int color) {
for (FormattedCharSequence formattedCharSequence : font.split(text, lineWidth)) {
this.drawString(font, formattedCharSequence, x, y, color, false);
y += 9;
}
}
public int drawStringWithBackdrop(Font font, Component text, int x, int y, int xOffset, int color) {
int i = this.minecraft.options.getBackgroundColor(0.0F);
if (i != 0) {
int j = 2;
this.fill(x - 2, y - 2, x + xOffset + 2, y + 9 + 2, ARGB.multiply(i, color));
}
return this.drawString(font, text, x, y, color, true);
}
/**
* Renders an outline rectangle on the screen with the specified color.
*
* @param x the x-coordinate of the top-left corner of the rectangle.
* @param y the y-coordinate of the top-left corner of the rectangle.
* @param width the width of the blitted portion.
* @param height the height of the rectangle.
* @param color the color of the outline.
*/
public void renderOutline(int x, int y, int width, int height, int color) {
this.fill(x, y, x + width, y + 1, color);
this.fill(x, y + height - 1, x + width, y + height, color);
this.fill(x, y + 1, x + 1, y + height - 1, color);
this.fill(x + width - 1, y + 1, x + width, y + height - 1, color);
}
public void blitSprite(Function<ResourceLocation, RenderType> function, ResourceLocation resourceLocation, int i, int j, int k, int l) {
this.blitSprite(function, resourceLocation, i, j, k, l, -1);
}
public void blitSprite(Function<ResourceLocation, RenderType> function, ResourceLocation resourceLocation, int i, int j, int k, int l, int m) {
TextureAtlasSprite textureAtlasSprite = this.sprites.getSprite(resourceLocation);
GuiSpriteScaling guiSpriteScaling = this.sprites.getSpriteScaling(textureAtlasSprite);
if (guiSpriteScaling instanceof GuiSpriteScaling.Stretch) {
this.blitSprite(function, textureAtlasSprite, i, j, k, l, m);
} else if (guiSpriteScaling instanceof GuiSpriteScaling.Tile tile) {
this.blitTiledSprite(function, textureAtlasSprite, i, j, k, l, 0, 0, tile.width(), tile.height(), tile.width(), tile.height(), m);
} else if (guiSpriteScaling instanceof GuiSpriteScaling.NineSlice nineSlice) {
this.blitNineSlicedSprite(function, textureAtlasSprite, nineSlice, i, j, k, l, m);
}
}
public void blitSprite(
Function<ResourceLocation, RenderType> function, ResourceLocation resourceLocation, int i, int j, int k, int l, int m, int n, int o, int p
) {
TextureAtlasSprite textureAtlasSprite = this.sprites.getSprite(resourceLocation);
GuiSpriteScaling guiSpriteScaling = this.sprites.getSpriteScaling(textureAtlasSprite);
if (guiSpriteScaling instanceof GuiSpriteScaling.Stretch) {
this.blitSprite(function, textureAtlasSprite, i, j, k, l, m, n, o, p, -1);
} else {
this.blitSprite(function, textureAtlasSprite, m, n, o, p);
}
}
public void blitSprite(Function<ResourceLocation, RenderType> function, TextureAtlasSprite textureAtlasSprite, int i, int j, int k, int l) {
this.blitSprite(function, textureAtlasSprite, i, j, k, l, -1);
}
public void blitSprite(Function<ResourceLocation, RenderType> function, TextureAtlasSprite textureAtlasSprite, int i, int j, int k, int l, int m) {
if (k != 0 && l != 0) {
this.innerBlit(
function,
textureAtlasSprite.atlasLocation(),
i,
i + k,
j,
j + l,
textureAtlasSprite.getU0(),
textureAtlasSprite.getU1(),
textureAtlasSprite.getV0(),
textureAtlasSprite.getV1(),
m
);
}
}
private void blitSprite(
Function<ResourceLocation, RenderType> function, TextureAtlasSprite textureAtlasSprite, int i, int j, int k, int l, int m, int n, int o, int p, int q
) {
if (o != 0 && p != 0) {
this.innerBlit(
function,
textureAtlasSprite.atlasLocation(),
m,
m + o,
n,
n + p,
textureAtlasSprite.getU((float)k / i),
textureAtlasSprite.getU((float)(k + o) / i),
textureAtlasSprite.getV((float)l / j),
textureAtlasSprite.getV((float)(l + p) / j),
q
);
}
}
private void blitNineSlicedSprite(
Function<ResourceLocation, RenderType> function,
TextureAtlasSprite textureAtlasSprite,
GuiSpriteScaling.NineSlice nineSlice,
int i,
int j,
int k,
int l,
int m
) {
Border border = nineSlice.border();
int n = Math.min(border.left(), k / 2);
int o = Math.min(border.right(), k / 2);
int p = Math.min(border.top(), l / 2);
int q = Math.min(border.bottom(), l / 2);
if (k == nineSlice.width() && l == nineSlice.height()) {
this.blitSprite(function, textureAtlasSprite, nineSlice.width(), nineSlice.height(), 0, 0, i, j, k, l, m);
} else if (l == nineSlice.height()) {
this.blitSprite(function, textureAtlasSprite, nineSlice.width(), nineSlice.height(), 0, 0, i, j, n, l, m);
this.blitNineSliceInnerSegment(
function,
nineSlice,
textureAtlasSprite,
i + n,
j,
k - o - n,
l,
n,
0,
nineSlice.width() - o - n,
nineSlice.height(),
nineSlice.width(),
nineSlice.height(),
m
);
this.blitSprite(function, textureAtlasSprite, nineSlice.width(), nineSlice.height(), nineSlice.width() - o, 0, i + k - o, j, o, l, m);
} else if (k == nineSlice.width()) {
this.blitSprite(function, textureAtlasSprite, nineSlice.width(), nineSlice.height(), 0, 0, i, j, k, p, m);
this.blitNineSliceInnerSegment(
function,
nineSlice,
textureAtlasSprite,
i,
j + p,
k,
l - q - p,
0,
p,
nineSlice.width(),
nineSlice.height() - q - p,
nineSlice.width(),
nineSlice.height(),
m
);
this.blitSprite(function, textureAtlasSprite, nineSlice.width(), nineSlice.height(), 0, nineSlice.height() - q, i, j + l - q, k, q, m);
} else {
this.blitSprite(function, textureAtlasSprite, nineSlice.width(), nineSlice.height(), 0, 0, i, j, n, p, m);
this.blitNineSliceInnerSegment(
function, nineSlice, textureAtlasSprite, i + n, j, k - o - n, p, n, 0, nineSlice.width() - o - n, p, nineSlice.width(), nineSlice.height(), m
);
this.blitSprite(function, textureAtlasSprite, nineSlice.width(), nineSlice.height(), nineSlice.width() - o, 0, i + k - o, j, o, p, m);
this.blitSprite(function, textureAtlasSprite, nineSlice.width(), nineSlice.height(), 0, nineSlice.height() - q, i, j + l - q, n, q, m);
this.blitNineSliceInnerSegment(
function,
nineSlice,
textureAtlasSprite,
i + n,
j + l - q,
k - o - n,
q,
n,
nineSlice.height() - q,
nineSlice.width() - o - n,
q,
nineSlice.width(),
nineSlice.height(),
m
);
this.blitSprite(
function, textureAtlasSprite, nineSlice.width(), nineSlice.height(), nineSlice.width() - o, nineSlice.height() - q, i + k - o, j + l - q, o, q, m
);
this.blitNineSliceInnerSegment(
function, nineSlice, textureAtlasSprite, i, j + p, n, l - q - p, 0, p, n, nineSlice.height() - q - p, nineSlice.width(), nineSlice.height(), m
);
this.blitNineSliceInnerSegment(
function,
nineSlice,
textureAtlasSprite,
i + n,
j + p,
k - o - n,
l - q - p,
n,
p,
nineSlice.width() - o - n,
nineSlice.height() - q - p,
nineSlice.width(),
nineSlice.height(),
m
);
this.blitNineSliceInnerSegment(
function,
nineSlice,
textureAtlasSprite,
i + k - o,
j + p,
n,
l - q - p,
nineSlice.width() - o,
p,
o,
nineSlice.height() - q - p,
nineSlice.width(),
nineSlice.height(),
m
);
}
}
private void blitNineSliceInnerSegment(
Function<ResourceLocation, RenderType> function,
GuiSpriteScaling.NineSlice nineSlice,
TextureAtlasSprite textureAtlasSprite,
int i,
int j,
int k,
int l,
int m,
int n,
int o,
int p,
int q,
int r,
int s
) {
if (k > 0 && l > 0) {
if (nineSlice.stretchInner()) {
this.innerBlit(
function,
textureAtlasSprite.atlasLocation(),
i,
i + k,
j,
j + l,
textureAtlasSprite.getU((float)m / q),
textureAtlasSprite.getU((float)(m + o) / q),
textureAtlasSprite.getV((float)n / r),
textureAtlasSprite.getV((float)(n + p) / r),
s
);
} else {
this.blitTiledSprite(function, textureAtlasSprite, i, j, k, l, m, n, o, p, q, r, s);
}
}
}
private void blitTiledSprite(
Function<ResourceLocation, RenderType> function,
TextureAtlasSprite textureAtlasSprite,
int i,
int j,
int k,
int l,
int m,
int n,
int o,
int p,
int q,
int r,
int s
) {
if (k > 0 && l > 0) {
if (o > 0 && p > 0) {
for (int t = 0; t < k; t += o) {
int u = Math.min(o, k - t);
for (int v = 0; v < l; v += p) {
int w = Math.min(p, l - v);
this.blitSprite(function, textureAtlasSprite, q, r, m, n, i + t, j + v, u, w, s);
}
}
} else {
throw new IllegalArgumentException("Tiled sprite texture size must be positive, got " + o + "x" + p);
}
}
}
public void blit(
Function<ResourceLocation, RenderType> function, ResourceLocation resourceLocation, int i, int j, float f, float g, int k, int l, int m, int n, int o
) {
this.blit(function, resourceLocation, i, j, f, g, k, l, k, l, m, n, o);
}
public void blit(
Function<ResourceLocation, RenderType> function, ResourceLocation resourceLocation, int i, int j, float f, float g, int k, int l, int m, int n
) {
this.blit(function, resourceLocation, i, j, f, g, k, l, k, l, m, n);
}
public void blit(
Function<ResourceLocation, RenderType> function, ResourceLocation resourceLocation, int i, int j, float f, float g, int k, int l, int m, int n, int o, int p
) {
this.blit(function, resourceLocation, i, j, f, g, k, l, m, n, o, p, -1);
}
public void blit(
Function<ResourceLocation, RenderType> function,
ResourceLocation resourceLocation,
int i,
int j,
float f,
float g,
int k,
int l,
int m,
int n,
int o,
int p,
int q
) {
this.innerBlit(function, resourceLocation, i, i + k, j, j + l, (f + 0.0F) / o, (f + m) / o, (g + 0.0F) / p, (g + n) / p, q);
}
private void innerBlit(
Function<ResourceLocation, RenderType> function, ResourceLocation resourceLocation, int i, int j, int k, int l, float f, float g, float h, float m, int n
) {
RenderType renderType = (RenderType)function.apply(resourceLocation);
Matrix4f matrix4f = this.pose.last().pose();
VertexConsumer vertexConsumer = this.bufferSource.getBuffer(renderType);
vertexConsumer.addVertex(matrix4f, (float)i, (float)k, 0.0F).setUv(f, h).setColor(n);
vertexConsumer.addVertex(matrix4f, (float)i, (float)l, 0.0F).setUv(f, m).setColor(n);
vertexConsumer.addVertex(matrix4f, (float)j, (float)l, 0.0F).setUv(g, m).setColor(n);
vertexConsumer.addVertex(matrix4f, (float)j, (float)k, 0.0F).setUv(g, h).setColor(n);
}
/**
* Renders an item stack at the specified coordinates.
*
* @param stack the item stack to render.
* @param x the x-coordinate of the rendering position.
* @param y the y-coordinate of the rendering position.
*/
public void renderItem(ItemStack stack, int x, int y) {
this.renderItem(this.minecraft.player, this.minecraft.level, stack, x, y, 0);
}
/**
* Renders an item stack at the specified coordinates with a random seed.
*
* @param stack the item stack to render.
* @param x the x-coordinate of the rendering position.
* @param y the y-coordinate of the rendering position.
* @param seed the random seed.
*/
public void renderItem(ItemStack stack, int x, int y, int seed) {
this.renderItem(this.minecraft.player, this.minecraft.level, stack, x, y, seed);
}
/**
* Renders an item stack at the specified coordinates with a random seed and a custom value.
*
* @param stack the item stack to render.
* @param x the x-coordinate of the rendering position.
* @param y the y-coordinate of the rendering position.
* @param seed the random seed.
* @param guiOffset the GUI offset.
*/
public void renderItem(ItemStack stack, int x, int y, int seed, int guiOffset) {
this.renderItem(this.minecraft.player, this.minecraft.level, stack, x, y, seed, guiOffset);
}
/**
* Renders a fake item stack at the specified coordinates.
*
* @param stack the fake item stack to render.
* @param x the x-coordinate of the rendering position.
* @param y the y-coordinate of the rendering position.
*/
public void renderFakeItem(ItemStack stack, int x, int y) {
this.renderFakeItem(stack, x, y, 0);
}
public void renderFakeItem(ItemStack stack, int x, int y, int seed) {
this.renderItem(null, this.minecraft.level, stack, x, y, seed);
}
/**
* Renders an item stack for a living entity at the specified coordinates with a random seed.
*
* @param entity the living entity.
* @param stack the item stack to render.
* @param x the x-coordinate of the rendering position.
* @param y the y-coordinate of the rendering position.
* @param seed the random seed.
*/
public void renderItem(LivingEntity entity, ItemStack stack, int x, int y, int seed) {
this.renderItem(entity, entity.level(), stack, x, y, seed);
}
/**
* Renders an item stack for a living entity in a specific level at the specified coordinates with a random seed.
*
* @param entity the living entity. Can be null.
* @param level the level in which the rendering occurs. Can be null.
* @param stack the item stack to render.
* @param x the x-coordinate of the rendering position.
* @param y the y-coordinate of the rendering position.
* @param seed the random seed.
*/
private void renderItem(@Nullable LivingEntity entity, @Nullable Level level, ItemStack stack, int x, int y, int seed) {
this.renderItem(entity, level, stack, x, y, seed, 0);
}
/**
* Renders an item stack for a living entity in a specific level at the specified coordinates with a random seed and a custom GUI offset.
*
* @param entity the living entity. Can be null.
* @param level the level in which the rendering occurs. Can be null.
* @param stack the item stack to render.
* @param x the x-coordinate of the rendering position.
* @param y the y-coordinate of the rendering position.
* @param seed the random seed.
* @param guiOffset the GUI offset value.
*/
private void renderItem(@Nullable LivingEntity entity, @Nullable Level level, ItemStack stack, int x, int y, int seed, int guiOffset) {
if (!stack.isEmpty()) {
BakedModel bakedModel = this.minecraft.getItemRenderer().getModel(stack, level, entity, seed);
this.pose.pushPose();
this.pose.translate((float)(x + 8), (float)(y + 8), (float)(150 + (bakedModel.isGui3d() ? guiOffset : 0)));
try {
this.pose.scale(16.0F, -16.0F, 16.0F);
boolean bl = !bakedModel.usesBlockLight();
if (bl) {
this.flush();
Lighting.setupForFlatItems();
}
if (stack.is(ItemTags.BUNDLES)) {
this.minecraft
.getItemRenderer()
.renderBundleItem(
stack, ItemDisplayContext.GUI, false, this.pose, this.bufferSource, 15728880, OverlayTexture.NO_OVERLAY, bakedModel, level, entity, seed
);
} else {
this.minecraft
.getItemRenderer()
.render(stack, ItemDisplayContext.GUI, false, this.pose, this.bufferSource, 15728880, OverlayTexture.NO_OVERLAY, bakedModel);
}
this.flush();
if (bl) {
Lighting.setupFor3DItems();
}
} catch (Throwable var12) {
CrashReport crashReport = CrashReport.forThrowable(var12, "Rendering item");
CrashReportCategory crashReportCategory = crashReport.addCategory("Item being rendered");
crashReportCategory.setDetail("Item Type", (CrashReportDetail<String>)(() -> String.valueOf(stack.getItem())));
crashReportCategory.setDetail("Item Components", (CrashReportDetail<String>)(() -> String.valueOf(stack.getComponents())));
crashReportCategory.setDetail("Item Foil", (CrashReportDetail<String>)(() -> String.valueOf(stack.hasFoil())));
throw new ReportedException(crashReport);
}
this.pose.popPose();
}
}
/**
* Renders additional decorations for an item stack at the specified coordinates.
*
* @param font the font used for rendering text.
* @param stack the item stack to decorate.
* @param x the x-coordinate of the rendering position.
* @param y the y-coordinate of the rendering position.
*/
public void renderItemDecorations(Font font, ItemStack stack, int x, int y) {
this.renderItemDecorations(font, stack, x, y, null);
}
/**
* Renders additional decorations for an item stack at the specified coordinates with optional custom text.
*
* @param font the font used for rendering text.
* @param stack the item stack to decorate.
* @param x the x-coordinate of the rendering position.
* @param y the y-coordinate of the rendering position.
* @param text the custom text to display. Can be null.
*/
public void renderItemDecorations(Font font, ItemStack stack, int x, int y, @Nullable String text) {
if (!stack.isEmpty()) {
this.pose.pushPose();
this.renderItemBar(stack, x, y);
this.renderItemCount(font, stack, x, y, text);
this.renderItemCooldown(stack, x, y);
this.pose.popPose();
}
}
/**
* Renders a tooltip for an item stack at the specified mouse coordinates.
*
* @param font the font used for rendering text.
* @param stack the item stack to display the tooltip for.
* @param mouseX the x-coordinate of the mouse position.
* @param mouseY the y-coordinate of the mouse position.
*/
public void renderTooltip(Font font, ItemStack stack, int mouseX, int mouseY) {
this.renderTooltip(font, Screen.getTooltipFromItem(this.minecraft, stack), stack.getTooltipImage(), mouseX, mouseY, stack.get(DataComponents.TOOLTIP_STYLE));
}
/**
* Renders a tooltip with customizable components at the specified mouse coordinates.
*
* @param font the font used for rendering text.
* @param tooltipLines the lines of the tooltip.
* @param visualTooltipComponent the visual tooltip component. Can be empty.
* @param mouseX the x-coordinate of the mouse position.
* @param mouseY the y-coordinate of the mouse position.
*/
public void renderTooltip(Font font, List<Component> tooltipLines, Optional<TooltipComponent> visualTooltipComponent, int mouseX, int mouseY) {
this.renderTooltip(font, tooltipLines, visualTooltipComponent, mouseX, mouseY, null);
}
public void renderTooltip(Font font, List<Component> list, Optional<TooltipComponent> optional, int i, int j, @Nullable ResourceLocation resourceLocation) {
List<ClientTooltipComponent> list2 = (List<ClientTooltipComponent>)list.stream()
.map(Component::getVisualOrderText)
.map(ClientTooltipComponent::create)
.collect(Util.toMutableList());
optional.ifPresent(tooltipComponent -> list2.add(list2.isEmpty() ? 0 : 1, ClientTooltipComponent.create(tooltipComponent)));
this.renderTooltipInternal(font, list2, i, j, DefaultTooltipPositioner.INSTANCE, resourceLocation);
}
/**
* Renders a tooltip with a single line of text at the specified mouse coordinates.
*
* @param font the font used for rendering text.
* @param text the text to display in the tooltip.
* @param mouseX the x-coordinate of the mouse position.
* @param mouseY the y-coordinate of the mouse position.
*/
public void renderTooltip(Font font, Component text, int mouseX, int mouseY) {
this.renderTooltip(font, text, mouseX, mouseY, null);
}
public void renderTooltip(Font font, Component component, int i, int j, @Nullable ResourceLocation resourceLocation) {
this.renderTooltip(font, List.of(component.getVisualOrderText()), i, j, resourceLocation);
}
/**
* Renders a tooltip with multiple lines of component-based text at the specified mouse coordinates.
*
* @param font the font used for rendering text.
* @param tooltipLines the lines of the tooltip as components.
* @param mouseX the x-coordinate of the mouse position.
* @param mouseY the y-coordinate of the mouse position.
*/
public void renderComponentTooltip(Font font, List<Component> tooltipLines, int mouseX, int mouseY) {
this.renderComponentTooltip(font, tooltipLines, mouseX, mouseY, null);
}
public void renderComponentTooltip(Font font, List<Component> list, int i, int j, @Nullable ResourceLocation resourceLocation) {
this.renderTooltipInternal(
font,
list.stream().map(Component::getVisualOrderText).map(ClientTooltipComponent::create).toList(),
i,
j,
DefaultTooltipPositioner.INSTANCE,
resourceLocation
);
}
/**
* Renders a tooltip with multiple lines of formatted text at the specified mouse coordinates.
*
* @param font the font used for rendering text.
* @param tooltipLines the lines of the tooltip as formatted character sequences.
* @param mouseX the x-coordinate of the mouse position.
* @param mouseY the y-coordinate of the mouse position.
*/
public void renderTooltip(Font font, List<? extends FormattedCharSequence> tooltipLines, int mouseX, int mouseY) {
this.renderTooltip(font, tooltipLines, mouseX, mouseY, null);
}
public void renderTooltip(Font font, List<? extends FormattedCharSequence> list, int i, int j, @Nullable ResourceLocation resourceLocation) {
this.renderTooltipInternal(
font,
(List<ClientTooltipComponent>)list.stream().map(ClientTooltipComponent::create).collect(Collectors.toList()),
i,
j,
DefaultTooltipPositioner.INSTANCE,
resourceLocation
);
}
/**
* Renders a tooltip with multiple lines of formatted text using a custom tooltip positioner at the specified mouse coordinates.
*
* @param font the font used for rendering text.
* @param tooltipLines the lines of the tooltip as formatted character sequences.
* @param tooltipPositioner the positioner to determine the tooltip's position.
* @param mouseX the x-coordinate of the mouse position.
* @param mouseY the y-coordinate of the mouse position.
*/
public void renderTooltip(Font font, List<FormattedCharSequence> tooltipLines, ClientTooltipPositioner tooltipPositioner, int mouseX, int mouseY) {
this.renderTooltipInternal(
font,
(List<ClientTooltipComponent>)tooltipLines.stream().map(ClientTooltipComponent::create).collect(Collectors.toList()),
mouseX,
mouseY,
tooltipPositioner,
null
);
}
private void renderTooltipInternal(
Font font, List<ClientTooltipComponent> list, int i, int j, ClientTooltipPositioner clientTooltipPositioner, @Nullable ResourceLocation resourceLocation
) {
if (!list.isEmpty()) {
int k = 0;
int l = list.size() == 1 ? -2 : 0;
for (ClientTooltipComponent clientTooltipComponent : list) {
int m = clientTooltipComponent.getWidth(font);
if (m > k) {
k = m;
}
l += clientTooltipComponent.getHeight(font);
}
int n = k;
int o = l;
Vector2ic vector2ic = clientTooltipPositioner.positionTooltip(this.guiWidth(), this.guiHeight(), i, j, k, l);
int p = vector2ic.x();
int q = vector2ic.y();
this.pose.pushPose();
int r = 400;
TooltipRenderUtil.renderTooltipBackground(this, p, q, k, l, 400, resourceLocation);
this.pose.translate(0.0F, 0.0F, 400.0F);
int s = q;
for (int t = 0; t < list.size(); t++) {
ClientTooltipComponent clientTooltipComponent2 = (ClientTooltipComponent)list.get(t);
clientTooltipComponent2.renderText(font, p, s, this.pose.last().pose(), this.bufferSource);
s += clientTooltipComponent2.getHeight(font) + (t == 0 ? 2 : 0);
}
s = q;
for (int t = 0; t < list.size(); t++) {
ClientTooltipComponent clientTooltipComponent2 = (ClientTooltipComponent)list.get(t);
clientTooltipComponent2.renderImage(font, p, s, n, o, this);
s += clientTooltipComponent2.getHeight(font) + (t == 0 ? 2 : 0);
}
this.pose.popPose();
}
}
private void renderItemBar(ItemStack itemStack, int i, int j) {
if (itemStack.isBarVisible()) {
int k = i + 2;
int l = j + 13;
this.fill(RenderType.gui(), k, l, k + 13, l + 2, 200, -16777216);
this.fill(RenderType.gui(), k, l, k + itemStack.getBarWidth(), l + 1, 200, ARGB.opaque(itemStack.getBarColor()));
}
}
private void renderItemCount(Font font, ItemStack itemStack, int i, int j, @Nullable String string) {
if (itemStack.getCount() != 1 || string != null) {
String string2 = string == null ? String.valueOf(itemStack.getCount()) : string;
this.pose.pushPose();
this.pose.translate(0.0F, 0.0F, 200.0F);
this.drawString(font, string2, i + 19 - 2 - font.width(string2), j + 6 + 3, -1, true);
this.pose.popPose();
}
}
private void renderItemCooldown(ItemStack itemStack, int i, int j) {
LocalPlayer localPlayer = this.minecraft.player;
float f = localPlayer == null
? 0.0F
: localPlayer.getCooldowns().getCooldownPercent(itemStack, this.minecraft.getDeltaTracker().getGameTimeDeltaPartialTick(true));
if (f > 0.0F) {
int k = j + Mth.floor(16.0F * (1.0F - f));
int l = k + Mth.ceil(16.0F * f);
this.fill(RenderType.gui(), i, k, i + 16, l, 200, Integer.MAX_VALUE);
}
}
/**
* Renders a hover effect for a text component at the specified mouse coordinates.
*
* @param font the font used for rendering text.
* @param style the style of the text component. Can be null.
* @param mouseX the x-coordinate of the mouse position.
* @param mouseY the y-coordinate of the mouse position.
*/
public void renderComponentHoverEffect(Font font, @Nullable Style style, int mouseX, int mouseY) {
if (style != null && style.getHoverEvent() != null) {
HoverEvent hoverEvent = style.getHoverEvent();
HoverEvent.ItemStackInfo itemStackInfo = hoverEvent.getValue(HoverEvent.Action.SHOW_ITEM);
if (itemStackInfo != null) {
this.renderTooltip(font, itemStackInfo.getItemStack(), mouseX, mouseY);
} else {
HoverEvent.EntityTooltipInfo entityTooltipInfo = hoverEvent.getValue(HoverEvent.Action.SHOW_ENTITY);
if (entityTooltipInfo != null) {
if (this.minecraft.options.advancedItemTooltips) {
this.renderComponentTooltip(font, entityTooltipInfo.getTooltipLines(), mouseX, mouseY);
}
} else {
Component component = hoverEvent.getValue(HoverEvent.Action.SHOW_TEXT);
if (component != null) {
this.renderTooltip(font, font.split(component, Math.max(this.guiWidth() / 2, 200)), mouseX, mouseY);
}
}
}
}
}
public void drawSpecial(Consumer<MultiBufferSource> consumer) {
consumer.accept(this.bufferSource);
this.bufferSource.endBatch();
}
/**
* A utility class for managing a stack of screen rectangles for scissoring.
*/
@Environment(EnvType.CLIENT)
static class ScissorStack {
private final Deque<ScreenRectangle> stack = new ArrayDeque();
/**
* Pushes a screen rectangle onto the scissor stack.
* <p>
* @return The resulting intersection of the pushed rectangle with the previous top rectangle on the stack, or the pushed rectangle if the stack is empty.
*
* @param scissor the screen rectangle to push.
*/
public ScreenRectangle push(ScreenRectangle scissor) {
ScreenRectangle screenRectangle = (ScreenRectangle)this.stack.peekLast();
if (screenRectangle != null) {
ScreenRectangle screenRectangle2 = (ScreenRectangle)Objects.requireNonNullElse(scissor.intersection(screenRectangle), ScreenRectangle.empty());
this.stack.addLast(screenRectangle2);
return screenRectangle2;
} else {
this.stack.addLast(scissor);
return scissor;
}
}
/**
* Pops the top screen rectangle from the scissor stack.
* <p>
* @return The new top screen rectangle after the pop operation, or null if the stack is empty.
* @throws IllegalStateException if the stack is empty.
*/
@Nullable
public ScreenRectangle pop() {
if (this.stack.isEmpty()) {
throw new IllegalStateException("Scissor stack underflow");
} else {
this.stack.removeLast();
return (ScreenRectangle)this.stack.peekLast();
}
}
public boolean containsPoint(int x, int y) {
return this.stack.isEmpty() ? true : ((ScreenRectangle)this.stack.peek()).containsPoint(x, y);
}
}
}