aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/cc/polyfrost/oneconfig/renderer
diff options
context:
space:
mode:
authorDeDiamondPro <67508414+DeDiamondPro@users.noreply.github.com>2022-06-05 17:43:23 +0200
committerGitHub <noreply@github.com>2022-06-05 17:43:23 +0200
commitab7256dff5d6d37488081ba7a01b36d3ee9ef563 (patch)
tree8207341e6c402848cdbe7b2f2297f5f975e0e083 /src/main/java/cc/polyfrost/oneconfig/renderer
parenta903cfc4d3f76cf3db24749b65156d126fa714e7 (diff)
downloadOneConfig-ab7256dff5d6d37488081ba7a01b36d3ee9ef563.tar.gz
OneConfig-ab7256dff5d6d37488081ba7a01b36d3ee9ef563.tar.bz2
OneConfig-ab7256dff5d6d37488081ba7a01b36d3ee9ef563.zip
refactor (#36)
* refactor * fix vig compat * fix nanovg thingy * e * finalize * gui utils package thingy
Diffstat (limited to 'src/main/java/cc/polyfrost/oneconfig/renderer')
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/renderer/RenderManager.java755
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/renderer/font/Font.java41
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/renderer/font/FontManager.java47
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/renderer/font/Fonts.java15
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/renderer/image/ImageLoader.java190
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/renderer/image/Images.java19
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/renderer/image/SVGs.java52
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/renderer/scissor/Scissor.java27
-rw-r--r--src/main/java/cc/polyfrost/oneconfig/renderer/scissor/ScissorManager.java69
9 files changed, 1215 insertions, 0 deletions
diff --git a/src/main/java/cc/polyfrost/oneconfig/renderer/RenderManager.java b/src/main/java/cc/polyfrost/oneconfig/renderer/RenderManager.java
new file mode 100644
index 0000000..20fc8f6
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/renderer/RenderManager.java
@@ -0,0 +1,755 @@
+package cc.polyfrost.oneconfig.renderer;
+
+import cc.polyfrost.oneconfig.gui.Colors;
+import cc.polyfrost.oneconfig.config.data.InfoType;
+import cc.polyfrost.oneconfig.gui.OneConfigGui;
+import cc.polyfrost.oneconfig.renderer.font.Font;
+import cc.polyfrost.oneconfig.renderer.font.FontManager;
+import cc.polyfrost.oneconfig.renderer.font.Fonts;
+import cc.polyfrost.oneconfig.renderer.image.ImageLoader;
+import cc.polyfrost.oneconfig.renderer.image.Images;
+import cc.polyfrost.oneconfig.renderer.image.SVGs;
+import cc.polyfrost.oneconfig.utils.InputUtils;
+import cc.polyfrost.oneconfig.utils.NetworkUtils;
+import gg.essential.universal.UGraphics;
+import gg.essential.universal.UMinecraft;
+import gg.essential.universal.UResolution;
+import net.minecraft.client.gui.Gui;
+import net.minecraft.client.shader.Framebuffer;
+import org.lwjgl.nanovg.NVGColor;
+import org.lwjgl.nanovg.NVGPaint;
+import org.lwjgl.opengl.GL11;
+
+import java.util.function.LongConsumer;
+
+import static org.lwjgl.nanovg.NanoVG.*;
+import static org.lwjgl.nanovg.NanoVGGL2.NVG_ANTIALIAS;
+import static org.lwjgl.nanovg.NanoVGGL2.nvgCreate;
+
+/**
+ * Handles NanoVG rendering and wraps it in a more convenient interface.
+ */
+public final class RenderManager {
+ private static long vg = -1;
+
+ //nanovg
+
+ private RenderManager() {
+
+ }
+
+ /**
+ * Sets up rendering, calls the consumer with the NanoVG context, and then cleans up.
+ *
+ * @param consumer The consumer to call.
+ * @see RenderManager#setupAndDraw(boolean, LongConsumer)
+ */
+ public static void setupAndDraw(LongConsumer consumer) {
+ setupAndDraw(false, consumer);
+ }
+
+ /**
+ * Sets up rendering, calls the consumer with the NanoVG context, and then cleans up.
+ *
+ * @param mcScaling Whether to render with Minecraft's scaling.
+ * @param consumer The consumer to call.
+ */
+ public static void setupAndDraw(boolean mcScaling, LongConsumer consumer) {
+ if (vg == -1) {
+ vg = nvgCreate(NVG_ANTIALIAS);
+ if (vg == -1) {
+ throw new RuntimeException("Failed to create nvg context");
+ }
+ FontManager.INSTANCE.initialize(vg);
+ }
+
+ Framebuffer fb = UMinecraft.getMinecraft().getFramebuffer();
+ if (!fb.isStencilEnabled()) {
+ fb.enableStencil();
+ }
+ GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
+ GL11.glDisable(GL11.GL_ALPHA_TEST);
+
+ if (mcScaling) {
+ nvgBeginFrame(vg, (float) UResolution.getScaledWidth(), (float) UResolution.getScaledHeight(), (float) UResolution.getScaleFactor());
+ } else {
+ // If we get blurry problems with high DPI monitors, 1 might need to be replaced with Display.getPixelScaleFactor()
+ nvgBeginFrame(vg, UResolution.getWindowWidth(), UResolution.getWindowHeight(), 1);
+ }
+
+ consumer.accept(vg);
+
+ nvgEndFrame(vg);
+
+ GL11.glPopAttrib();
+ }
+
+ /**
+ * Draws a rectangle with the given parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param height The height.
+ * @param color The color.
+ */
+ public static void drawRectangle(long vg, float x, float y, float width, float height, int color) { // TODO make everything use this one day
+ if (Colors.ROUNDED_CORNERS) {
+ drawRoundedRect(vg, x, y, width, height, color, Colors.CORNER_RADIUS);
+ } else {
+ drawRect(vg, x, y, width, height, color);
+ }
+ }
+
+ /**
+ * Draws a rectangle with the given parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param height The height.
+ * @param color The color.
+ */
+ public static void drawRect(long vg, float x, float y, float width, float height, int color) {
+ nvgBeginPath(vg);
+ nvgRect(vg, x, y, width, height);
+ NVGColor nvgColor = color(vg, color);
+ nvgFill(vg);
+ nvgColor.free();
+ }
+
+ /**
+ * Draws a rounded rectangle with the given parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param height The height.
+ * @param color The color.
+ * @param radius The radius.
+ */
+ public static void drawRoundedRect(long vg, float x, float y, float width, float height, int color, float radius) {
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x, y, width, height, radius);
+ color(vg, color);
+ NVGColor nvgColor = color(vg, color);
+ nvgFill(vg);
+ nvgColor.free();
+ }
+
+ /**
+ * Draw a rounded rectangle where every corner has a different radius
+ *
+ * @param vg The NanoVG context
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param height The height.
+ * @param color The color.
+ * @param radiusTL Top left corner radius.
+ * @param radiusTR Top right corner radius.
+ * @param radiusBR Bottom right corner radius.
+ * @param radiusBL Bottom left corner radius
+ */
+ public static void drawRoundedRectVaried(long vg, float x, float y, float width, float height, int color, float radiusTL, float radiusTR, float radiusBR, float radiusBL) {
+ nvgBeginPath(vg);
+ nvgRoundedRectVarying(vg, x, y, width, height, radiusTL, radiusTR, radiusBR, radiusBL);
+ color(vg, color);
+ NVGColor nvgColor = color(vg, color);
+ nvgFill(vg);
+ nvgColor.free();
+ }
+
+ /**
+ * Draws a hollow rounded rectangle with the given parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param height The height.
+ * @param color The color.
+ * @param radius The radius.
+ * @param thickness The thickness.
+ */
+ public static void drawHollowRoundRect(long vg, float x, float y, float width, float height, int color, float radius, float thickness) {
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x + thickness, y + thickness, width - thickness, height - thickness, radius);
+ nvgStrokeWidth(vg, thickness + 0.5f);
+ nvgPathWinding(vg, NVG_HOLE);
+ color(vg, color);
+ NVGColor nvgColor = color(vg, color);
+ nvgStrokeColor(vg, nvgColor);
+ nvgStroke(vg);
+ nvgColor.free();
+ }
+
+ /**
+ * Draws a gradient rectangle with the given parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param height The height.
+ * @param color The first color of the gradient.
+ * @param color2 The second color of the gradient.
+ */
+ public static void drawGradientRect(long vg, float x, float y, float width, float height, int color, int color2) {
+ NVGPaint bg = NVGPaint.create();
+ nvgBeginPath(vg);
+ nvgRect(vg, x, y, width, height);
+ NVGColor nvgColor = color(vg, color);
+ NVGColor nvgColor2 = color(vg, color2);
+ nvgFillPaint(vg, nvgLinearGradient(vg, x, y, x, y + width, nvgColor, nvgColor2, bg));
+ nvgFillPaint(vg, bg);
+ nvgFill(vg);
+ nvgColor.free();
+ nvgColor2.free();
+ }
+
+ /**
+ * Draws a rounded gradient rectangle with the given parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param height The height.
+ * @param color The first color of the gradient.
+ * @param color2 The second color of the gradient.
+ * @param radius The corner radius.
+ */
+ public static void drawGradientRoundedRect(long vg, float x, float y, float width, float height, int color, int color2, float radius) {
+ NVGPaint bg = NVGPaint.create();
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x, y, width, height, radius);
+ NVGColor nvgColor = color(vg, color);
+ NVGColor nvgColor2 = color(vg, color2);
+ nvgFillPaint(vg, nvgLinearGradient(vg, x, y, x + width, y, nvgColor, nvgColor2, bg));
+ nvgFill(vg);
+ nvgColor.free();
+ nvgColor2.free();
+ }
+
+ /**
+ * Draw a HSB box
+ *
+ * @param vg The NanoVG context.
+ * @param x The x coordinate.
+ * @param y The y coordinate
+ * @param width The width.
+ * @param height The height.
+ * @param colorTarget Hue color
+ */
+ public static void drawHSBBox(long vg, float x, float y, float width, float height, int colorTarget) {
+ drawRoundedRect(vg, x, y, width, height, colorTarget, 8f);
+
+ NVGPaint bg = NVGPaint.create();
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x, y, width, height, 8f);
+ NVGColor nvgColor = color(vg, -1);
+ NVGColor nvgColor2 = color(vg, Colors.TRANSPARENT);
+ nvgFillPaint(vg, nvgLinearGradient(vg, x, y, x + width, y, nvgColor, nvgColor2, bg));
+ nvgFill(vg);
+ nvgColor.free();
+ nvgColor2.free();
+
+ NVGPaint bg2 = NVGPaint.create();
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x, y, width, height, 8f);
+ NVGColor nvgColor3 = color(vg, Colors.TRANSPARENT);
+ NVGColor nvgColor4 = color(vg, Colors.BLACK);
+ nvgFillPaint(vg, nvgLinearGradient(vg, x, y, x, y + height, nvgColor3, nvgColor4, bg2));
+ nvgFill(vg);
+ nvgColor3.free();
+ nvgColor4.free();
+ }
+
+ /**
+ * Draws a circle with the given parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param x The x position.
+ * @param y The y position.
+ * @param radius The radius.
+ * @param color The color.
+ */
+ public static void drawCircle(long vg, float x, float y, float radius, int color) {
+ nvgBeginPath(vg);
+ nvgCircle(vg, x, y, radius);
+ NVGColor nvgColor = color(vg, color);
+ nvgFill(vg);
+ nvgColor.free();
+ }
+
+ /**
+ * Draws a String with the given parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param text The text.
+ * @param x The x position.
+ * @param y The y position.
+ * @param color The color.
+ * @param size The size.
+ * @param font The font.
+ * @see cc.polyfrost.oneconfig.renderer.font.Font
+ */
+ public static void drawText(long vg, String text, float x, float y, int color, float size, Fonts font) {
+ drawText(vg, text, x, y, color, size, font.font);
+ }
+
+ /**
+ * Draws a String with the given parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param text The text.
+ * @param x The x position.
+ * @param y The y position.
+ * @param color The color.
+ * @param size The size.
+ * @param font The font.
+ * @see cc.polyfrost.oneconfig.renderer.font.Font
+ */
+ public static void drawText(long vg, String text, float x, float y, int color, float size, Font font) {
+ nvgBeginPath(vg);
+ nvgFontSize(vg, size);
+ nvgFontFace(vg, font.getName());
+ nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
+ NVGColor nvgColor = color(vg, color);
+ nvgText(vg, x, y, text);
+ nvgFill(vg);
+ nvgColor.free();
+ }
+
+ /**
+ * Draws a String wrapped at the given width, with the given parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param text The text.
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param color The color.
+ * @param size The size.
+ * @param font The font.
+ */
+ public static void drawWrappedString(long vg, String text, float x, float y, float width, int color, float size, Fonts font) {
+ drawWrappedString(vg, text, x, y, width, color, size, font.font);
+ }
+
+ /**
+ * Draws a String wrapped at the given width, with the given parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param text The text.
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param color The color.
+ * @param size The size.
+ * @param font The font.
+ */
+ public static void drawWrappedString(long vg, String text, float x, float y, float width, int color, float size, Font font) {
+ nvgBeginPath(vg);
+ nvgFontSize(vg, size);
+ nvgFontFace(vg, font.getName());
+ nvgTextAlign(vg, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE);
+ NVGColor nvgColor = color(vg, color);
+ nvgTextBox(vg, x, y, width, text);
+ nvgFill(vg);
+ nvgColor.free();
+ }
+
+ /**
+ * Draw a formatted URL (a string in blue with an underline) that when clicked, opens the given text.
+ *
+ * <p><b>This does NOT scale to Minecraft's GUI scale!</b></p>
+ *
+ * @see RenderManager#drawText(long, String, float, float, int, float, Font)
+ * @see InputUtils#isAreaClicked(int, int, int, int)
+ */
+ public static void drawURL(long vg, String url, float x, float y, float size, Fonts font) {
+ drawURL(vg, url, x, y, size, font.font);
+ }
+
+ /**
+ * Draw a formatted URL (a string in blue with an underline) that when clicked, opens the given text.
+ *
+ * <p><b>This does NOT scale to Minecraft's GUI scale!</b></p>
+ *
+ * @see RenderManager#drawText(long, String, float, float, int, float, Font)
+ * @see InputUtils#isAreaClicked(int, int, int, int)
+ */
+ public static void drawURL(long vg, String url, float x, float y, float size, Font font) {
+ drawText(vg, url, x, y, Colors.PRIMARY_500, size, font);
+ float length = getTextWidth(vg, url, size, font);
+ drawRectangle(vg, x, y + size / 2, length, 1, Colors.PRIMARY_500);
+ if (InputUtils.isAreaClicked((int) (x - 2), (int) (y - 1), (int) (length + 4), (int) (size / 2 + 3))) {
+ NetworkUtils.browseLink(url);
+ }
+ }
+
+ /**
+ * Draws an image with the provided file path.
+ *
+ * @param vg The NanoVG context.
+ * @param filePath The file path.
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param height The height.
+ * @see RenderManager#drawImage(long, String, float, float, float, float, int)
+ */
+ public static void drawImage(long vg, String filePath, float x, float y, float width, float height) {
+ if (ImageLoader.INSTANCE.loadImage(vg, filePath)) {
+ NVGPaint imagePaint = NVGPaint.calloc();
+ int image = ImageLoader.INSTANCE.getImage(filePath);
+ nvgBeginPath(vg);
+ nvgImagePattern(vg, x, y, width, height, 0, image, 1, imagePaint);
+ nvgRect(vg, x, y, width, height);
+ nvgFillPaint(vg, imagePaint);
+ nvgFill(vg);
+ imagePaint.free();
+ }
+ }
+
+ /**
+ * Draws an image with the provided file path.
+ *
+ * @param vg The NanoVG context.
+ * @param filePath The file path.
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param height The height.
+ * @param color The color.
+ */
+ public static void drawImage(long vg, String filePath, float x, float y, float width, float height, int color) {
+ if (ImageLoader.INSTANCE.loadImage(vg, filePath)) {
+ NVGPaint imagePaint = NVGPaint.calloc();
+ int image = ImageLoader.INSTANCE.getImage(filePath);
+ nvgBeginPath(vg);
+ nvgImagePattern(vg, x, y, width, height, 0, image, 1, imagePaint);
+ nvgRGBA((byte) (color >> 16 & 0xFF), (byte) (color >> 8 & 0xFF), (byte) (color & 0xFF), (byte) (color >> 24 & 0xFF), imagePaint.innerColor());
+ nvgRect(vg, x, y, width, height);
+ nvgFillPaint(vg, imagePaint);
+ nvgFill(vg);
+ imagePaint.free();
+ }
+ }
+
+ /**
+ * Draws an image with the provided file path and parameters.
+ *
+ * @see RenderManager#drawImage(long, String, float, float, float, float)
+ */
+ public static void drawImage(long vg, Images filePath, float x, float y, float width, float height) {
+ drawImage(vg, filePath.filePath, x, y, width, height);
+ }
+
+ /**
+ * Draws an image with the provided file path and parameters.
+ *
+ * @see RenderManager#drawImage(long, String, float, float, float, float, int)
+ */
+ public static void drawImage(long vg, Images filePath, float x, float y, float width, float height, int color) {
+ drawImage(vg, filePath.filePath, x, y, width, height, color);
+ }
+
+ /**
+ * Draws a rounded image with the provided file path and parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param filePath The file path.
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param height The height.
+ * @param radius The radius.
+ */
+ public static void drawRoundImage(long vg, String filePath, float x, float y, float width, float height, float radius) {
+ if (ImageLoader.INSTANCE.loadImage(vg, filePath)) {
+ NVGPaint imagePaint = NVGPaint.calloc();
+ int image = ImageLoader.INSTANCE.getImage(filePath);
+ nvgBeginPath(vg);
+ nvgImagePattern(vg, x, y, width, height, 0, image, 1, imagePaint);
+ nvgRoundedRect(vg, x, y, width, height, radius);
+ nvgFillPaint(vg, imagePaint);
+ nvgFill(vg);
+ imagePaint.free();
+ }
+ }
+
+ /**
+ * Draws a rounded image with the provided file path and parameters.
+ *
+ * @see RenderManager#drawRoundImage(long, String, float, float, float, float, float)
+ */
+ public static void drawRoundImage(long vg, Images filePath, float x, float y, float width, float height, float radius) {
+ drawRoundImage(vg, filePath.filePath, x, y, width, height, radius);
+ }
+
+ public static float getTextWidth(long vg, String text, float fontSize, Fonts font) {
+ return getTextWidth(vg, text, fontSize, font.font);
+ }
+
+ /**
+ * Get the width of the provided String.
+ *
+ * @param vg The NanoVG context.
+ * @param text The text.
+ * @param fontSize The font size.
+ * @param font The font.
+ * @return The width of the text.
+ */
+ public static float getTextWidth(long vg, String text, float fontSize, Font font) {
+ float[] bounds = new float[4];
+ nvgFontSize(vg, fontSize);
+ nvgFontFace(vg, font.getName());
+ return nvgTextBounds(vg, 0, 0, text, bounds);
+ }
+
+ /**
+ * Draws a line with the provided parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param x The x position.
+ * @param y The y position.
+ * @param endX The end x position.
+ * @param endY The end y position.
+ * @param width The width.
+ * @param color The color.
+ */
+ public static void drawLine(long vg, float x, float y, float endX, float endY, float width, int color) {
+ nvgBeginPath(vg);
+ nvgMoveTo(vg, x, y);
+ nvgLineTo(vg, endX, endY);
+ NVGColor nvgColor = color(vg, color);
+ nvgStrokeColor(vg, nvgColor);
+ nvgStrokeWidth(vg, width);
+ nvgStroke(vg);
+ nvgColor.free();
+ }
+
+ /**
+ * Draw a drop shadow.
+ *
+ * <a href="https://github.com/SpinyOwl/legui/blob/develop/LICENSE">Adapted from legui under MIT license</a>
+ *
+ * @param vg The NanoVG context.
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @param w The width.
+ * @param h The height.
+ * @param blur The blur (feather).
+ * @param spread The spread.
+ * @param cornerRadius The radius of the corner
+ */
+ public static void drawDropShadow(long vg, float x, float y, float w, float h, float blur, float spread, float cornerRadius) {
+ try (NVGPaint shadowPaint = NVGPaint.calloc(); // allocating memory to pass color to nanovg wrapper
+ NVGColor firstColor = NVGColor.calloc(); // allocating memory to pass color to nanovg wrapper
+ NVGColor secondColor = NVGColor.calloc() // allocating memory to pass color to nanovg wrapper
+ ) {
+ fillNVGColorWithRGBA(0, 0, 0, 0.5f, firstColor); // filling allocated memory
+ fillNVGColorWithRGBA(0, 0, 0, 0, secondColor); // filling allocated memory
+
+ // creating gradient and put it to shadowPaint
+ nvgBoxGradient(vg, x - spread, y - spread, w + 2 * spread, h + 2 * spread, cornerRadius + spread, blur, firstColor, secondColor, shadowPaint);
+ nvgBeginPath(vg);
+ nvgRoundedRect(vg, x - spread - blur, y - spread - blur, w + 2 * spread + 2 * blur, h + 2 * spread + 2 * blur, cornerRadius + spread);
+ nvgRoundedRect(vg, x, y, w, h, cornerRadius);
+ nvgPathWinding(vg, NVG_HOLE);
+ nvgFillPaint(vg, shadowPaint);
+ nvgFill(vg);
+ }
+ }
+
+ /**
+ * Fills the provided {@link NVGColor} with the provided RGBA values.
+ *
+ * @param r The red value.
+ * @param g The green value.
+ * @param b The blue value.
+ * @param a The alpha value.
+ * @param color The {@link NVGColor} to fill.
+ */
+ public static void fillNVGColorWithRGBA(float r, float g, float b, float a, NVGColor color) {
+ color.r(r);
+ color.g(g);
+ color.b(b);
+ color.a(a);
+ }
+
+ /**
+ * Create a {@link NVGColor} from the provided RGBA values.
+ *
+ * @param vg The NanoVG context.
+ * @param color The color.
+ * @return The {@link NVGColor} created.
+ */
+ public static NVGColor color(long vg, int color) {
+ NVGColor nvgColor = NVGColor.calloc();
+ nvgRGBA((byte) (color >> 16 & 0xFF), (byte) (color >> 8 & 0xFF), (byte) (color & 0xFF), (byte) (color >> 24 & 0xFF), nvgColor);
+ nvgFillColor(vg, nvgColor);
+ return nvgColor;
+ }
+
+ /**
+ * Scales all rendering by the provided scale.
+ *
+ * @param vg The NanoVG context.
+ * @param x The x scale.
+ * @param y The y scale.
+ */
+ public static void scale(long vg, float x, float y) {
+ nvgScale(vg, x, y);
+ }
+
+ /**
+ * Sets the global alpha value to render with.
+ *
+ * @param vg The NanoVG context.
+ * @param alpha The alpha value.
+ */
+ public static void setAlpha(long vg, float alpha) {
+ nvgGlobalAlpha(vg, alpha);
+ }
+
+ /**
+ * Draws a SVG with the provided file path and parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param filePath The file path.
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param height The height.
+ */
+ public static void drawSvg(long vg, String filePath, float x, float y, float width, float height) {
+ float w = width;
+ float h = height;
+ if (OneConfigGui.INSTANCE != null) {
+ w *= OneConfigGui.INSTANCE.getScaleFactor();
+ h *= OneConfigGui.INSTANCE.getScaleFactor();
+ }
+ if (ImageLoader.INSTANCE.loadSVG(vg, filePath, w, h)) {
+ NVGPaint imagePaint = NVGPaint.calloc();
+ int image = ImageLoader.INSTANCE.getSVG(filePath, w, h);
+ nvgBeginPath(vg);
+ nvgImagePattern(vg, x, y, width, height, 0, image, 1, imagePaint);
+ nvgRect(vg, x, y, width, height);
+ nvgFillPaint(vg, imagePaint);
+ nvgFill(vg);
+ imagePaint.free();
+ }
+ }
+
+ /**
+ * Draws a SVG with the provided file path and parameters.
+ *
+ * @param vg The NanoVG context.
+ * @param filePath The file path.
+ * @param x The x position.
+ * @param y The y position.
+ * @param width The width.
+ * @param height The height.
+ * @param color The color.
+ */
+ public static void drawSvg(long vg, String filePath, float x, float y, float width, float height, int color) {
+ float w = width;
+ float h = height;
+ if (OneConfigGui.INSTANCE != null) {
+ w *= OneConfigGui.INSTANCE.getScaleFactor();
+ h *= OneConfigGui.INSTANCE.getScaleFactor();
+ }
+ if (ImageLoader.INSTANCE.loadSVG(vg, filePath, w, h)) {
+ NVGPaint imagePaint = NVGPaint.calloc();
+ int image = ImageLoader.INSTANCE.getSVG(filePath, w, h);
+ nvgBeginPath(vg);
+ nvgImagePattern(vg, x, y, width, height, 0, image, 1, imagePaint);
+ nvgRGBA((byte) (color >> 16 & 0xFF), (byte) (color >> 8 & 0xFF), (byte) (color & 0xFF), (byte) (color >> 24 & 0xFF), imagePaint.innerColor());
+ nvgRect(vg, x, y, width, height);
+ nvgFillPaint(vg, imagePaint);
+ nvgFill(vg);
+ imagePaint.free();
+ }
+ }
+
+ /**
+ * Draws an SVG with the provided file path and parameters.
+ *
+ * @see RenderManager#drawSvg(long, String, float, float, float, float)
+ */
+ public static void drawSvg(long vg, SVGs svg, float x, float y, float width, float height) {
+ drawSvg(vg, svg.filePath, x, y, width, height);
+ }
+
+ /**
+ * Draws an SVG with the provided file path and parameters.
+ *
+ * @see RenderManager#drawSvg(long, String, float, float, float, float, int)
+ */
+ public static void drawSvg(long vg, SVGs svg, float x, float y, float width, float height, int color) {
+ drawSvg(vg, svg.filePath, x, y, width, height, color);
+ }
+
+ /**
+ * Draw a circle with an info icon inside of it
+ *
+ * @param vg The NanoVG context.
+ * @param type The icon type.
+ * @param x The x position.
+ * @param y The y position.
+ * @param size The diameter.
+ */
+ public static void drawInfo(long vg, InfoType type, float x, float y, float size) {
+ SVGs icon = null;
+ int colorOuter = 0;
+ int colorInner = 0;
+ switch (type) {
+ case INFO:
+ icon = SVGs.INFO_CIRCLE;
+ colorOuter = Colors.GRAY_400;
+ colorInner = Colors.GRAY_300;
+ break;
+ case SUCCESS:
+ icon = SVGs.CHECK_CIRCLE;
+ colorOuter = Colors.SUCCESS_700;
+ colorInner = Colors.SUCCESS_600;
+ break;
+ case WARNING:
+ icon = SVGs.WARNING;
+ colorOuter = Colors.WARNING_600;
+ colorInner = Colors.WARNING_500;
+ break;
+ case ERROR:
+ icon = SVGs.ERROR;
+ colorOuter = Colors.ERROR_700;
+ colorInner = Colors.ERROR_600;
+ break;
+ }
+ float centerX = x + size / 2f;
+ float centerY = y + size / 2f;
+ drawCircle(vg, centerX, centerY, size / 2, colorOuter);
+ drawCircle(vg, centerX, centerY, size / 2 - size / 12, colorInner);
+ float iconSize = size / 1.75f;
+ drawSvg(vg, icon, centerX - iconSize / 2f, centerY - iconSize / 2f, iconSize, iconSize);
+ }
+
+ // gl
+
+ public static void drawScaledString(String text, float x, float y, int color, boolean shadow, float scale) {
+ UGraphics.GL.pushMatrix();
+ UGraphics.GL.scale(scale, scale, 1);
+ UMinecraft.getFontRenderer().drawString(text, x * (1 / scale), y * (1 / scale), color, shadow);
+ UGraphics.GL.popMatrix();
+ }
+
+ public static void drawGlRect(int x, int y, int width, int height, int color) {
+ Gui.drawRect(x, y, x + width, y + height, color);
+ }
+}
diff --git a/src/main/java/cc/polyfrost/oneconfig/renderer/font/Font.java b/src/main/java/cc/polyfrost/oneconfig/renderer/font/Font.java
new file mode 100644
index 0000000..b35708b
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/renderer/font/Font.java
@@ -0,0 +1,41 @@
+package cc.polyfrost.oneconfig.renderer.font;
+
+import java.nio.ByteBuffer;
+
+public class Font {
+ private final String fileName;
+ private final String name;
+ private boolean loaded = false;
+ private ByteBuffer buffer = null;
+
+ public Font(String name, String fileName) {
+ this.name = name;
+ this.fileName = fileName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getFileName() {
+ return fileName;
+ }
+
+ public boolean isLoaded() {
+ return loaded;
+ }
+
+ void setLoaded(boolean loaded) {
+ this.loaded = loaded;
+ }
+
+ public ByteBuffer getBuffer() {
+ return buffer;
+ }
+
+ void setBuffer(ByteBuffer buffer) {
+ this.buffer = buffer;
+ }
+
+}
+
diff --git a/src/main/java/cc/polyfrost/oneconfig/renderer/font/FontManager.java b/src/main/java/cc/polyfrost/oneconfig/renderer/font/FontManager.java
new file mode 100644
index 0000000..388911d
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/renderer/font/FontManager.java
@@ -0,0 +1,47 @@
+package cc.polyfrost.oneconfig.renderer.font;
+
+import cc.polyfrost.oneconfig.utils.IOUtils;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import static org.lwjgl.nanovg.NanoVG.nvgCreateFontMem;
+
+public class FontManager {
+ public static FontManager INSTANCE = new FontManager();
+
+ /**
+ * Load all fonts in the Fonts class
+ *
+ * @param vg NanoVG context
+ */
+
+ public void initialize(long vg) {
+ for (Fonts fonts : Fonts.values()) {
+ loadFont(vg, fonts.font);
+ }
+ }
+
+ /**
+ * Load a font into NanoVG
+ *
+ * @param vg NanoVG context
+ * @param font The font to be loaded
+ */
+ public void loadFont(long vg, Font font) {
+ if (font.isLoaded()) return;
+ int loaded = -1;
+ try {
+ ByteBuffer buffer = IOUtils.resourceToByteBuffer(font.getFileName());
+ loaded = nvgCreateFontMem(vg, font.getName(), buffer, 0);
+ font.setBuffer(buffer);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ if (loaded == -1) {
+ throw new RuntimeException("Failed to initialize font " + font.getName());
+ } else {
+ font.setLoaded(true);
+ }
+ }
+}
diff --git a/src/main/java/cc/polyfrost/oneconfig/renderer/font/Fonts.java b/src/main/java/cc/polyfrost/oneconfig/renderer/font/Fonts.java
new file mode 100644
index 0000000..9b6193f
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/renderer/font/Fonts.java
@@ -0,0 +1,15 @@
+package cc.polyfrost.oneconfig.renderer.font;
+
+public enum Fonts {
+ BOLD(new Font("inter-bold", "/assets/oneconfig/font/Bold.otf")),
+ SEMIBOLD(new Font("inter-semibold", "/assets/oneconfig/font/SemiBold.otf")),
+ MEDIUM(new Font("inter-medium", "/assets/oneconfig/font/Medium.otf")),
+ REGULAR(new Font("inter-regular", "/assets/oneconfig/font/Regular.otf")),
+ MINECRAFT_REGULAR(new Font("mc-regular", "/assets/oneconfig/font/Minecraft-Regular.otf")),
+ MINECRAFT_BOLD(new Font("mc-bold", "/assets/oneconfig/font/Minecraft-Bold.otf"));
+ public final Font font;
+
+ Fonts(Font font) {
+ this.font = font;
+ }
+}
diff --git a/src/main/java/cc/polyfrost/oneconfig/renderer/image/ImageLoader.java b/src/main/java/cc/polyfrost/oneconfig/renderer/image/ImageLoader.java
new file mode 100644
index 0000000..e8861eb
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/renderer/image/ImageLoader.java
@@ -0,0 +1,190 @@
+package cc.polyfrost.oneconfig.renderer.image;
+
+import cc.polyfrost.oneconfig.utils.IOUtils;
+import org.lwjgl.nanovg.NSVGImage;
+import org.lwjgl.nanovg.NanoSVG;
+import org.lwjgl.nanovg.NanoVG;
+import org.lwjgl.stb.STBImage;
+import org.lwjgl.system.MemoryUtil;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+
+/**
+ * Loads images and SVGs from resources into NanoVG.
+ *
+ * @see cc.polyfrost.oneconfig.renderer.RenderManager
+ * @see Images
+ * @see SVGs
+ */
+public final class ImageLoader {
+ private ImageLoader() {
+
+ }
+
+ private final HashMap<String, Integer> imageHashMap = new HashMap<>();
+ private final HashMap<String, Integer> svgHashMap = new HashMap<>();
+ public static ImageLoader INSTANCE = new ImageLoader();
+
+ /**
+ * Loads an image from resources.
+ *
+ * @param vg The NanoVG context.
+ * @param fileName The name of the file to load.
+ * @return Whether the image was loaded successfully.
+ */
+ public boolean loadImage(long vg, String fileName) {
+ if (!imageHashMap.containsKey(fileName)) {
+ int[] width = {0};
+ int[] height = {0};
+ int[] channels = {0};
+
+ ByteBuffer image = IOUtils.resourceToByteBufferNullable(fileName);
+ if (image == null) {
+ return false;
+ }
+
+ ByteBuffer buffer = STBImage.stbi_load_from_memory(image, width, height, channels, 4);
+ if (buffer == null) {
+ return false;
+ }
+
+ imageHashMap.put(fileName, NanoVG.nvgCreateImageRGBA(vg, width[0], height[0], NanoVG.NVG_IMAGE_REPEATX | NanoVG.NVG_IMAGE_REPEATY | NanoVG.NVG_IMAGE_GENERATE_MIPMAPS, buffer));
+ return true;
+ }
+ return true;
+ }
+
+ /**
+ * Loads an SVG from resources.
+ *
+ * @param vg The NanoVG context.
+ * @param fileName The name of the file to load.
+ * @param width The width of the SVG.
+ * @param height The height of the SVG.
+ * @return Whether the SVG was loaded successfully.
+ */
+ public boolean loadSVG(long vg, String fileName, float width, float height) {
+ String name = fileName + "-" + width + "-" + height;
+ if (!svgHashMap.containsKey(name)) {
+ try {
+ InputStream inputStream = this.getClass().getResourceAsStream(fileName);
+ if (inputStream == null) return false;
+ StringBuilder resultStringBuilder = new StringBuilder();
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ resultStringBuilder.append(line);
+ }
+ }
+ CharSequence s = resultStringBuilder.toString();
+ NSVGImage svg = NanoSVG.nsvgParse(s, "px", 96f);
+ if (svg == null) return false;
+ long rasterizer = NanoSVG.nsvgCreateRasterizer();
+
+ int w = (int) svg.width();
+ int h = (int) svg.height();
+ float scale = Math.max(width / w, height / h);
+ w = (int) (w * scale);
+ h = (int) (h * scale);
+
+ ByteBuffer image = MemoryUtil.memAlloc(w * h * 4);
+ NanoSVG.nsvgRasterize(rasterizer, svg, 0, 0, scale, image, w, h, w * 4);
+
+ NanoSVG.nsvgDeleteRasterizer(rasterizer);
+ NanoSVG.nsvgDelete(svg);
+
+ svgHashMap.put(name, NanoVG.nvgCreateImageRGBA(vg, w, h, NanoVG.NVG_IMAGE_REPEATX | NanoVG.NVG_IMAGE_REPEATY | NanoVG.NVG_IMAGE_GENERATE_MIPMAPS, image));
+ return true;
+ } catch (Exception e) {
+ System.err.println("Failed to parse SVG file");
+ e.printStackTrace();
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get a loaded image from the cache.
+ * <p><b>Requires the image to have been loaded first.</b></p>
+ *
+ * @param fileName The name of the file to load.
+ * @return The image
+ * @see ImageLoader#loadImage(long, String)
+ */
+ public int getImage(String fileName) {
+ return imageHashMap.get(fileName);
+ }
+
+ /**
+ * Remove an image from the cache, allowing the image to be garbage collected.
+ * Should be used when the GUI rendering the image is closed.
+ *
+ * @param vg The NanoVG context.
+ * @param fileName The name of the file to remove.
+ * @see ImageLoader#loadImage(long, String)
+ */
+ public void removeImage(long vg, String fileName) {
+ NanoVG.nvgDeleteImage(vg, imageHashMap.get(fileName));
+ imageHashMap.remove(fileName);
+ }
+
+ /**
+ * Clears all images from the cache, allowing the images cleared to be garbage collected.
+ * Should be used when the GUI rendering loaded images are closed.
+ *
+ * @param vg The NanoVG context.
+ */
+ public void clearImages(long vg) {
+ HashMap<String, Integer> temp = new HashMap<>(imageHashMap);
+ for (String image : temp.keySet()) {
+ NanoVG.nvgDeleteImage(vg, imageHashMap.get(image));
+ imageHashMap.remove(image);
+ }
+ }
+
+ /**
+ * Get a loaded SVG from the cache.
+ * <p><b>Requires the SVG to have been loaded first.</b></p>
+ *
+ * @param fileName The name of the file to load.
+ * @return The SVG
+ * @see ImageLoader#loadSVG(long, String, float, float)
+ */
+ public int getSVG(String fileName, float width, float height) {
+ String name = fileName + "-" + width + "-" + height;
+ return svgHashMap.get(name);
+ }
+
+ /**
+ * Remove a SVG from the cache, allowing the SVG to be garbage collected.
+ * Should be used when the GUI rendering the SVG is closed.
+ *
+ * @param vg The NanoVG context.
+ * @param fileName The name of the file to remove.
+ * @see ImageLoader#loadSVG(long, String, float, float)
+ */
+ public void removeSVG(long vg, String fileName, float width, float height) {
+ String name = fileName + "-" + width + "-" + height;
+ NanoVG.nvgDeleteImage(vg, imageHashMap.get(name));
+ svgHashMap.remove(name);
+ }
+
+ /**
+ * Clears all SVGs from the cache, allowing the SVGs cleared to be garbage collected.
+ * Should be used when the GUI rendering loaded SVGs are closed.
+ *
+ * @param vg The NanoVG context.
+ */
+ public void clearSVGs(long vg) {
+ HashMap<String, Integer> temp = new HashMap<>(svgHashMap);
+ for (String image : temp.keySet()) {
+ NanoVG.nvgDeleteImage(vg, svgHashMap.get(image));
+ svgHashMap.remove(image);
+ }
+ }
+}
diff --git a/src/main/java/cc/polyfrost/oneconfig/renderer/image/Images.java b/src/main/java/cc/polyfrost/oneconfig/renderer/image/Images.java
new file mode 100644
index 0000000..ad1941e
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/renderer/image/Images.java
@@ -0,0 +1,19 @@
+package cc.polyfrost.oneconfig.renderer.image;
+
+/**
+ * An enum of images used in OneConfig.
+ *
+ * @see cc.polyfrost.oneconfig.renderer.RenderManager#drawImage(long, String, float, float, float, float, int)
+ * @see ImageLoader
+ */
+public enum Images {
+ HUE_GRADIENT("/assets/oneconfig/options/HueGradient.png"),
+ COLOR_WHEEL("/assets/oneconfig/options/ColorWheel.png"),
+ ALPHA_GRID("/assets/oneconfig/options/AlphaGrid.png");
+
+ public final String filePath;
+
+ Images(String filePath) {
+ this.filePath = filePath;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/cc/polyfrost/oneconfig/renderer/image/SVGs.java b/src/main/java/cc/polyfrost/oneconfig/renderer/image/SVGs.java
new file mode 100644
index 0000000..5ba3fcd
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/renderer/image/SVGs.java
@@ -0,0 +1,52 @@
+package cc.polyfrost.oneconfig.renderer.image;
+
+/**
+ * An enum of SVGs used in OneConfig.
+ *
+ * @see cc.polyfrost.oneconfig.renderer.RenderManager#drawSvg(long, String, float, float, float, float, int)
+ * @see ImageLoader
+ */
+public enum SVGs {
+ ONECONFIG("/assets/oneconfig/icons/OneConfig.svg"),
+ ONECONFIG_OFF("/assets/oneconfig/icons/OneConfigOff.svg"),
+ COPYRIGHT_FILL("/assets/oneconfig/icons/CopyrightFill.svg"),
+ APERTURE_FILL("/assets/oneconfig/icons/ApertureFill.svg"),
+ ARROWS_CLOCKWISE_BOLD("/assets/oneconfig/icons/ArrowsClockwiseBold.svg"),
+ FADERS_HORIZONTAL_BOLD("/assets/oneconfig/icons/FadersHorizontalBold.svg"),
+ GAUGE_FILL("/assets/oneconfig/icons/GaugeFill.svg"),
+ GEAR_SIX_FILL("/assets/oneconfig/icons/GearSixFill.svg"),
+ MAGNIFYING_GLASS_BOLD("/assets/oneconfig/icons/MagnifyingGlassBold.svg"),
+ NOTE_PENCIL_BOLD("/assets/oneconfig/icons/NotePencilBold.svg"),
+ PAINT_BRUSH_BROAD_FILL("/assets/oneconfig/icons/PaintBrushBroadFill.svg"),
+ USER_SWITCH_FILL("/assets/oneconfig/icons/UserSwitchFill.svg"),
+ X_CIRCLE_BOLD("/assets/oneconfig/icons/XCircleBold.svg"),
+ CARET_LEFT("/assets/oneconfig/icons/CaretLeftBold.svg"),
+ CARET_RIGHT("/assets/oneconfig/icons/CaretRightBold.svg"),
+
+ // OLD ICONS
+ BOX("/assets/oneconfig/old-icons/Box.svg"),
+ CHECKBOX_TICK("/assets/oneconfig/old-icons/CheckboxTick.svg"),
+ CHECK_CIRCLE("/assets/oneconfig/old-icons/CheckCircle.svg"),
+ CHEVRON_DOWN("/assets/oneconfig/old-icons/ChevronDown.svg"),
+ CHEVRON_UP("/assets/oneconfig/old-icons/ChevronUp.svg"),
+ COPY("/assets/oneconfig/old-icons/Copy.svg"),
+ DROPDOWN_LIST("/assets/oneconfig/old-icons/DropdownList.svg"),
+ ERROR("/assets/oneconfig/old-icons/Error.svg"),
+ EYE("/assets/oneconfig/old-icons/Eye.svg"),
+ EYE_OFF("/assets/oneconfig/old-icons/EyeOff.svg"),
+ HEART_FILL("/assets/oneconfig/old-icons/HeartFill.svg"),
+ HEART_OUTLINE("/assets/oneconfig/old-icons/HeartOutline.svg"),
+ HELP_CIRCLE("/assets/oneconfig/old-icons/HelpCircle.svg"),
+ HISTORY("/assets/oneconfig/old-icons/History.svg"),
+ INFO_CIRCLE("/assets/oneconfig/old-icons/InfoCircle.svg"),
+ KEYSTROKE("/assets/oneconfig/old-icons/Keystroke.svg"),
+ PASTE("/assets/oneconfig/old-icons/Paste.svg"),
+ POP_OUT("/assets/oneconfig/old-icons/PopOut.svg"),
+ WARNING("/assets/oneconfig/old-icons/Warning.svg");
+
+ public final String filePath;
+
+ SVGs(String filePath) {
+ this.filePath = filePath;
+ }
+}
diff --git a/src/main/java/cc/polyfrost/oneconfig/renderer/scissor/Scissor.java b/src/main/java/cc/polyfrost/oneconfig/renderer/scissor/Scissor.java
new file mode 100644
index 0000000..179a260
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/renderer/scissor/Scissor.java
@@ -0,0 +1,27 @@
+package cc.polyfrost.oneconfig.renderer.scissor;
+
+/**
+ * A class that represents a scissor rectangle.
+ *
+ * @see ScissorManager
+ */
+public class Scissor {
+ public float x;
+ public float y;
+ public float width;
+ public float height;
+
+ public Scissor(float x, float y, float width, float height) {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ }
+
+ public Scissor(Scissor scissor) {
+ this.x = scissor.x;
+ this.y = scissor.y;
+ this.width = scissor.width;
+ this.height = scissor.height;
+ }
+}
diff --git a/src/main/java/cc/polyfrost/oneconfig/renderer/scissor/ScissorManager.java b/src/main/java/cc/polyfrost/oneconfig/renderer/scissor/ScissorManager.java
new file mode 100644
index 0000000..1f7801b
--- /dev/null
+++ b/src/main/java/cc/polyfrost/oneconfig/renderer/scissor/ScissorManager.java
@@ -0,0 +1,69 @@
+package cc.polyfrost.oneconfig.renderer.scissor;
+
+import org.lwjgl.nanovg.NanoVG;
+
+import java.util.ArrayList;
+
+/**
+ * Provides an easy way to manage and group scissor rectangles.
+ */
+public class ScissorManager {
+ private static final ArrayList<Scissor> scissors = new ArrayList<>();
+
+ /**
+ * Adds and applies a scissor rectangle to the list of scissor rectangles.
+ *
+ * @param vg The NanoVG context.
+ * @param x The x coordinate of the scissor rectangle.
+ * @param y The y coordinate of the scissor rectangle.
+ * @param width The width of the scissor rectangle.
+ * @param height The height of the scissor rectangle.
+ * @return The scissor rectangle.
+ */
+ public static Scissor scissor(long vg, float x, float y, float width, float height) {
+ Scissor scissor = new Scissor(x, y, width, height);
+ if (scissors.contains(scissor)) return scissor;
+ scissors.add(scissor);
+ applyScissors(vg);
+ return scissor;
+ }
+
+ /**
+ * Resets the scissor rectangle provided.
+ *
+ * @param vg The NanoVG context.
+ * @param scissor The scissor rectangle to reset.
+ */
+ public static void resetScissor(long vg, Scissor scissor) {
+ if (scissors.contains(scissor)) {
+ scissors.remove(scissor);
+ applyScissors(vg);
+ }
+ }
+
+ /**
+ * Clear all scissor rectangles.
+ *
+ * @param vg The NanoVG context.
+ */
+ public static void clearScissors(long vg) {
+ scissors.clear();
+ NanoVG.nvgResetScissor(vg);
+ }
+
+ private static void applyScissors(long vg) {
+ NanoVG.nvgResetScissor(vg);
+ if (scissors.size() <= 0) return;
+ Scissor finalScissor = new Scissor(scissors.get(0));
+ for (int i = 1; i < scissors.size(); i++) {
+ Scissor scissor = scissors.get(i);
+ float rightX = Math.min(scissor.x + scissor.width, finalScissor.x + finalScissor.width);
+ float rightY = Math.min(scissor.y + scissor.height, finalScissor.y + finalScissor.height);
+ finalScissor.x = Math.max(finalScissor.x, scissor.x);
+ finalScissor.y = Math.max(finalScissor.y, scissor.y);
+ finalScissor.width = rightX - finalScissor.x;
+ finalScissor.height = rightY - finalScissor.y;
+ }
+ NanoVG.nvgScissor(vg, finalScissor.x, finalScissor.y, finalScissor.width, finalScissor.height);
+ }
+}