diff --git a/base/src/main/java/de/staropensource/engine/base/type/Color.java b/base/src/main/java/de/staropensource/engine/base/type/Color.java new file mode 100644 index 000000000..a7f496077 --- /dev/null +++ b/base/src/main/java/de/staropensource/engine/base/type/Color.java @@ -0,0 +1,300 @@ +/* + * STAROPENSOURCE ENGINE SOURCE FILE + * Copyright (c) 2024 The StarOpenSource Engine Authors + * Licensed under the GNU Affero General Public License v3 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.staropensource.engine.base.type; + +import de.staropensource.engine.base.EngineConfiguration; +import de.staropensource.engine.base.utility.Math; +import lombok.Getter; +import lombok.Setter; +import lombok.SneakyThrows; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Range; + +import java.nio.ByteBuffer; +import java.util.HexFormat; +import java.util.Locale; + +/** + * A class dedicated to colors. + * Uses the RGBA format, but can also interpret + * the RGB, HEX and HSV color formats. + * + * @since v1-alpha6 + */ +@Getter +@Setter +@SuppressWarnings({ "JavadocDeclaration" }) +public final class Color { + /** + * Contains the red color value. + * + * @since v1-alpha6 + * -- GETTER -- + * Returns the red color value. + * + * @return red color value + * @since v1-alpha6 + * -- SETTER -- + * Sets the red color value. + * + * @param red new red color value + * @since v1-alpha6 + */ + private @Range(from = 0, to = 255) int red; + /** + * Contains the green color value. + * + * @since v1-alpha6 + * -- GETTER -- + * Returns the green color value. + * + * @return green color value + * @since v1-alpha6 + * -- SETTER -- + * Sets the green color value. + * + * @param green new green color value + * @since v1-alpha6 + */ + private @Range(from = 0, to = 255) int green; + /** + * Contains the blue color value. + * + * @since v1-alpha6 + * -- GETTER -- + * Returns the blue color value. + * + * @return blue color value + * @since v1-alpha6 + * -- SETTER -- + * Sets the blue color value. + * + * @param blue new blue color value + * @since v1-alpha6 + */ + private @Range(from = 0, to = 255) int blue; + /** + * Contains the alpha channel value. + * + * @since v1-alpha6 + * -- GETTER -- + * Returns the alpha channel value. + * + * @return alpha channel value + * @since v1-alpha6 + * -- SETTER -- + * Sets the alpha channel value. + * + * @param red new alpha channel value + * @since v1-alpha6 + */ + private @Range(from = 0, to = 255) int alpha; + + /** + * Creates a new instance. + * + * @param red red color value + * @param green green color value + * @param blue blue color value + * @param alpha alpha channel value + * @since v1-alpha6 + */ + private Color(int red, int green, int blue, int alpha) { + this.red = Math.boundNumber(0, 255, red); + this.green = Math.boundNumber(0, 255, green); + this.blue = Math.boundNumber(0, 255, blue); + this.alpha = Math.boundNumber(0, 255, alpha); + } + + /** + * Converts the a set of numbers in + * the RGBA format into a new instance. + * + * @param red red color value + * @param blue blue color value + * @param green green color value + * @param alpha alpha color value + * @return new {@link Color} instance + * @since v1-alpha6 + */ + public static @NotNull Color fromRGBA(@Range(from = 0, to = 255) int red, @Range(from = 0, to = 255) int green, + @Range(from = 0, to = 255) int blue, @Range(from = 0, to = 255) int alpha) { + return new Color(red, green, blue, alpha); + } + + /** + * Converts the an array of numbers in + * the RGBA format into a new instance. + * + * @param intArray integer array + * @return new {@link Color} instance + * @throws IndexOutOfBoundsException if the array contains more or less than four integers + * @since v1-alpha6 + */ + public static @NotNull Color fromRGBA(int @NotNull [] intArray) { + if (intArray.length != 4) + throw new StringIndexOutOfBoundsException("Can't contains more or less than four integers"); + + return new Color(intArray[0], intArray[1], intArray[2], intArray[3]); + } + + /** + * Converts the a set of numbers in + * the RGB format into a new instance. + * + * @param red red color value + * @param blue blue color value + * @param green green color value + * @return new {@link Color} instance + * @since v1-alpha6 + */ + public static @NotNull Color fromRGB(@Range(from = 0, to = 255) int red, @Range(from = 0, to = 255) int green, + @Range(from = 0, to = 255) int blue) { + return new Color(red, green, blue, 255); + } + + /** + * Converts the an array of numbers in + * the RGBA format into a new instance. + * + * @param intArray integer array + * @return new {@link Color} instance + * @throws IndexOutOfBoundsException if the array contains more or less than four integers + * @since v1-alpha6 + */ + public static @NotNull Color fromRGB(int @NotNull [] intArray) { + if (intArray.length != 3) + throw new StringIndexOutOfBoundsException("Can't contains more or less than four integers"); + + return new Color(intArray[0], intArray[1], intArray[2], 255); + } + + /** + * Converts an array of bytes into a new instance. + * + * @param bytes byte array + * @return new {@link Color} instance + * @throws IndexOutOfBoundsException if the array contains less than three or more than four bytes + * @since v1-alpha6 + */ + public static @NotNull Color fromBytes(byte @NotNull [] bytes) throws IndexOutOfBoundsException { + if (bytes.length == 3) + return new Color(bytes[0] & 0xFF, bytes[1] & 0xFF, bytes[2] & 0xFF, 255); + else if (bytes.length == 4) + return new Color(bytes[0] & 0xFF, bytes[1] & 0xFF, bytes[2] & 0xFF, bytes[3] & 0xFF); + else + throw new StringIndexOutOfBoundsException("Can't contain less than three or more than four bytes"); + } + + /** + * Converts a hex string into a new instance. + * + * @param hexString hex string + * @return new {@link Color} instance + * @throws IndexOutOfBoundsException if the string contains less than three or more than four bytes + * @since v1-alpha6 + */ + public static @NotNull Color fromHex(@NotNull String hexString) throws IndexOutOfBoundsException { + return fromBytes(HexFormat.of().parseHex(hexString)); + } + + /** + * Creates an identical copy of this instance. + * + * @return identical copy + * @since v1-alpha6 + */ + @SneakyThrows + public @NotNull Color clone() { + return (Color) super.clone(); + } + + /** + * Returns a string representation of this instance. + * + * @return string representation + * @since v1-alpha6 + */ + @Override + public @NotNull String toString() { + return (EngineConfiguration.getInstance().isHideFullTypePath() + ? getClass().getName().replace(getClass().getPackage() + ".", "") + : getClass().getName()) + + "(r=" + red + " g=" + green + " b=" + blue + " a=" + alpha + ")"; + } + + /** + * Converts the colors represented by + * this instance into an integer array + * in the RGBA format. + * + * @return {@code int} array with RGBA values + * @since v1-alpha6 + */ + public int @NotNull [] toRGBA() { + return new int[]{ red, green, blue, alpha }; + } + + /** + * Converts the colors represented by + * this instance into an integer array + * in the RGB format. + * + * @return {@code int} array with RGB values + * @since v1-alpha6 + */ + public int @NotNull [] toRGB() { + return new int[]{ red, green, blue }; + } + + /** + * Converts the colors represented by + * this instance into a byte array. + * + * @param includeAlpha whether to include alpha or not + * @return RGBA or RGB format as a byte array + * @since v1-alpha6 + */ + public byte @NotNull [] toBytes(boolean includeAlpha) { + ByteBuffer buffer = ByteBuffer.allocate(includeAlpha ? 4 : 3); + + buffer + .put((byte) red) + .put((byte) green) + .put((byte) blue); + + if (includeAlpha) + buffer.put((byte) alpha); + + return buffer.array(); + } + + /** + * Converts the colors represented by + * this instance into the hex format. + * + * @param includeAlpha whether to include alpha or not + * @return RGBA or RGB format as a hex string + * @since v1-alpha6 + */ + public @NotNull String toHex(boolean includeAlpha) { + return HexFormat.of().formatHex(toBytes(includeAlpha)).toUpperCase(Locale.ROOT); + } +}