diff --git a/src/main/java/com/pixelgamelibrary/api/graphics/Color.java b/src/main/java/com/pixelgamelibrary/api/graphics/Color.java
index f2272c9..90f7e4e 100644
--- a/src/main/java/com/pixelgamelibrary/api/graphics/Color.java
+++ b/src/main/java/com/pixelgamelibrary/api/graphics/Color.java
@@ -19,12 +19,14 @@
///////////////////////////////////////////////////////////////////////////////////////////////
package com.pixelgamelibrary.api.graphics;
+import com.pixelgamelibrary.api.Pixel;
import com.pixelgamelibrary.api.PixelException;
import static com.pixelgamelibrary.api.graphics.ColorDepth.BITS_16;
import static com.pixelgamelibrary.api.graphics.ColorDepth.BITS_24;
import static com.pixelgamelibrary.api.graphics.ColorDepth.BITS_32;
import static com.pixelgamelibrary.api.graphics.ColorDepth.BITS_4;
import static com.pixelgamelibrary.api.graphics.ColorDepth.BITS_8;
+import java.util.BitSet;
import lombok.Data;
/**
@@ -55,7 +57,8 @@ import lombok.Data;
@Data
public final class Color {
- private static final int BITS_IN_ONE_BYTE = 255;
+ private static final int EIGHT_BITS_TO_DECIMAL_WITHOUT_ONE = 255;
+
private float red, green, blue, alpha;
@@ -75,15 +78,15 @@ public final class Color {
return this;
}
- private static int floatElementToInt(float f) {
+ static int floatElementToInt(float f) {
- return (int) (f * BITS_IN_ONE_BYTE);
+ return (int) (f * EIGHT_BITS_TO_DECIMAL_WITHOUT_ONE);
}
- private static float intElementToFloat(int i) {
+ static float intElementToFloat(int i) {
float iFloat = i;
- return iFloat / BITS_IN_ONE_BYTE;
+ return iFloat / EIGHT_BITS_TO_DECIMAL_WITHOUT_ONE;
}
// Counts distance between two colors
@@ -433,10 +436,8 @@ public final class Color {
* right to discard the lower bits. This method modifies the existing color
* instance.
*/
- public void set16Bit() {
- this.red = floatElementToInt(this.red) >> 3; // 5 bits for red
- this.green = floatElementToInt(this.green) >> 2; // 6 bits for green
- this.blue = floatElementToInt(this.blue) >> 3; // 5 bits for blue
+ public void convertTo16Bit() {
+ convertToXBit(ColorDepth.BITS_16);
}
/**
@@ -446,10 +447,8 @@ public final class Color {
* blue (maximum value of 3). This method modifies the existing color
* instance.
*/
- public void set8Bit() {
- this.red = floatElementToInt(this.red) >> 5; // 3 bits for red (max 7)
- this.green = floatElementToInt(this.green) >> 5; // 3 bits for green (max 7)
- this.blue = floatElementToInt(this.blue) >> 6; // 2 bits for blue (max 3)
+ public void convertTo8Bit() {
+ convertToXBit(ColorDepth.BITS_8);
}
/**
@@ -458,33 +457,59 @@ public final class Color {
* achieved by shifting the integer values right to discard the lower bits.
* This method modifies the existing color instance.
*/
- public void set4Bit() {
- this.red = floatElementToInt(this.red) >> 1; // 2 bits for red
- this.green = floatElementToInt(this.green) >> 1; // 2 bits for green
- this.blue = floatElementToInt(this.blue) >> 1; // 2 bits for blue
+ public void convertTo4Bit() {
+ set(Color4BitPalette.findClosestColor(this));
+ }
+
+ private void convertToXBit(ColorDepth colorDepth) {
+ convertToXBit(colorDepth.getRedBitCount(), colorDepth.getGreenBitCount(), colorDepth.getBlueBitCount());
+ }
+
+ private void convertToXBit(int rBits, int gBits, int bBits) {
+ ColorDepthHelper colorDepthHelper = new ColorDepthHelper(this, rBits, gBits, bBits);
+ final int r = colorDepthHelper.r;
+ this.red = intElementToFloat(r);
+ final int g = colorDepthHelper.g;
+ this.green = intElementToFloat(g);
+ final int b = colorDepthHelper.b;
+ this.blue = intElementToFloat(b);
+ }
+
+ public void convertToWhiteBlack8Bit() {
+ convertToWhiteBlackXBit(8);
}
- public void setWhiteBlack8Bit() {
- int grayscale = (int) ((red + green + blue) / 3 * 255);
- this.red = this.green = this.blue = grayscale / 255f;
+ public void convertToWhiteBlack4Bit() {
+ convertToWhiteBlackXBit(4);
+ }
+
+ public void convertToWhiteBlack2Bit() {
+ convertToWhiteBlackXBit(2);
}
- public void setWhiteBlack4Bit() {
- int grayscale = (int) ((red + green + blue) / 3 * 15);
- this.red = this.green = this.blue = grayscale / 15f;
+ public void convertToWhiteBlack1Bit() {
+ convertToWhiteBlackXBit(1);
}
- public void setWhiteBlack2Bit() {
- int grayscale = (int) ((red + green + blue) / 3 * 3);
- this.red = this.green = this.blue = grayscale / 3f;
+ private void convertToWhiteBlackXBit(int bitCount) {
+ this.red = this.green = this.blue = colorToGreyScaleElement(this, bitCount);
+ }
+
+ private static float colorToGreyScaleElement(Color color, int bitCount) {
+ // Calculate the maximum value based on the bit count
+ int maxValue = (1 << bitCount) - 1; // 2^bitCount - 1
+
+ // Calculate the average of the color components and normalize to 255
+ float grayscale = (color.getRed() + color.getGreen() + color.getBlue()) / 3.0f;
+
+ // Normalize the grayscale value to the range [0, maxValue]
+ return (grayscale / 255.0f) * maxValue; // Scale to [0, maxValue]
}
- public void setWhiteBlack1Bit() {
- int grayscale = (int) ((red + green + blue) / 3 * 1);
- this.red = this.green = this.blue = grayscale / 1f;
- }
- public void setWithBitCount(int bitCount) {
+
+
+ public void convertToWithBitCount(int bitCount) {
ColorDepth colorDepth = ColorDepth.from(bitCount);
switch (colorDepth) {
case BITS_32:
@@ -492,82 +517,104 @@ public final class Color {
case BITS_24:
return;
case BITS_16:
- set16Bit();
+ convertTo16Bit();
break;
case BITS_8:
- set8Bit();
+ convertTo8Bit();
break;
case BITS_4:
- set4Bit();
+ convertTo4Bit();
break;
default:
throw new PixelException("Unsupported color depth: " + bitCount);
}
}
- public void setWhiteBlackWithBitCount(int bitCount) {
+ public void convertToWhiteBlackWithBitCount(int bitCount) {
switch (bitCount) {
case 8:
- setWhiteBlack8Bit();
+ convertToWhiteBlack8Bit();
break;
case 4:
- setWhiteBlack4Bit();
+ convertToWhiteBlack4Bit();
break;
case 2:
- setWhiteBlack2Bit();
+ convertToWhiteBlack2Bit();
break;
case 1:
- setWhiteBlack1Bit();
+ convertToWhiteBlack1Bit();
break;
default:
throw new PixelException("Unsupported bit count: " + bitCount);
}
}
- public void set(ColorMode colorMode, int bitCount) {
+ public void convertTo(ColorMode colorMode, int bitCount) {
if (colorMode == ColorMode.COLOR) {
- setWithBitCount(bitCount);
+ convertToWithBitCount(bitCount);
return;
}
if (colorMode == ColorMode.BLACK_AND_WHITE) {
- setWhiteBlackWithBitCount(bitCount);
+ convertToWhiteBlackWithBitCount(bitCount);
return;
}
throw new PixelException("Unsupported ColorMode: " + colorMode);
}
- public byte[] getByteRepresentation32Bit() {
- return null;//todo
+ public BitSet getByteRepresentation32Bit() {
+ return getByteRepresentation(ColorMode.COLOR, 32);
}
- public byte[] getByteRepresentation24Bit() {
- return null;//todo
+ public BitSet getByteRepresentation24Bit() {
+ return getByteRepresentation(ColorMode.COLOR, 24);
}
- public byte[] getByteRepresentation16Bit() {
- return null;//todo
+ public BitSet getByteRepresentation16Bit() {
+ return getByteRepresentation(ColorMode.COLOR, 16);
}
- public byte[] getByteRepresentation8Bit() {
- return null;//todo
+ public BitSet getByteRepresentation8Bit() {
+ return getByteRepresentation(ColorMode.COLOR, 8);
}
- public byte[] getByteRepresentation4Bit() {
- return null;//todo
+ public BitSet getByteRepresentation4Bit() {
+ return getByteRepresentation(ColorMode.COLOR, 4);
+ }
+
+ public BitSet getWhiteBlackByteRepresentation8Bit() {
+ return getByteRepresentation(ColorMode.BLACK_AND_WHITE, 8);
}
- public byte[] getByteRepresentation2Bit() {
- return null;//todo
+ public BitSet getWhiteBlackByteRepresentation4Bit() {
+ return getByteRepresentation(ColorMode.BLACK_AND_WHITE, 4);
}
- public byte[] getByteRepresentation1Bit() {
- return null;//todo
+ public BitSet getWhiteBlackByteRepresentation2Bit() {
+ return getByteRepresentation(ColorMode.BLACK_AND_WHITE, 2);
}
- public byte[] getByteRepresentation(ColorMode colorMode, int bitCount) {
- return null;//todo
+ public BitSet getWhiteBlackByteRepresentation1Bit() {
+ return getByteRepresentation(ColorMode.BLACK_AND_WHITE, 1);
+ }
+
+ public BitSet getByteRepresentation(ColorMode colorMode, int bitCount) {
+ if (colorMode == ColorMode.COLOR) {
+ Color colorClone = this.copy();
+ colorClone.convertTo(colorMode, bitCount);
+ ColorDepthHelper colorDepthHelper = new ColorDepthHelper(colorClone, ColorDepth.from(bitCount));
+ return colorDepthHelper.getBitSet();
+ }
+
+ if (colorMode == ColorMode.BLACK_AND_WHITE) {
+ Color colorClone = this.copy();
+ colorClone.convertTo(colorMode, bitCount);
+ float element = colorToGreyScaleElement(colorClone, bitCount);
+ double number = Math.pow(2, bitCount) * element;
+ return Pixel.utils().binary().convertIntToBitSet((int) number, bitCount);
+ }
+ throw new PixelException("Unsupported ColorMode: " + colorMode);
}
@Override
diff --git a/src/main/java/com/pixelgamelibrary/api/graphics/Color4BitPalette.java b/src/main/java/com/pixelgamelibrary/api/graphics/Color4BitPalette.java
index 7e45c68..0b2a539 100644
--- a/src/main/java/com/pixelgamelibrary/api/graphics/Color4BitPalette.java
+++ b/src/main/java/com/pixelgamelibrary/api/graphics/Color4BitPalette.java
@@ -18,7 +18,7 @@ package com.pixelgamelibrary.api.graphics;
// along with this program. If not, see
// or write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-///////////////////////////////////////////////////////////////////////////////////////////////package com.pixelgamelibrary.api.graphics;
+///////////////////////////////////////////////////////////////////////////////////////////////
/**
*
* @author robertvokac
diff --git a/src/main/java/com/pixelgamelibrary/api/graphics/ColorDepth.java b/src/main/java/com/pixelgamelibrary/api/graphics/ColorDepth.java
index 77f83ce..fb7912c 100644
--- a/src/main/java/com/pixelgamelibrary/api/graphics/ColorDepth.java
+++ b/src/main/java/com/pixelgamelibrary/api/graphics/ColorDepth.java
@@ -27,17 +27,33 @@ import lombok.Getter;
* @author robertvokac
*/
public enum ColorDepth {
- BITS_32(32),
- BITS_24(24),
- BITS_16(16),
- BITS_8(8),
+ BITS_32(32, 8,8,8),
+ BITS_24(24,8,8,8),
+ BITS_16(16,5,6,5),
+ BITS_8(8,3,3,2),
BITS_4(4);
-
+
@Getter
private final int bitCount;
+ @Getter
+ private final int redBitCount, greenBitCount, blueBitCount;
- ColorDepth(int bitCount) {
+ ColorDepth(int bitCount, int redBitCount, int greenBitCount, int blueBitCount) {
+ int sum = (redBitCount + greenBitCount + blueBitCount);
+ if(sum == 24 && bitCount == 32) {
+ sum = 32;
+ }
+ if(sum != 0 && bitCount != sum) {
+ throw new PixelException("Fatal internal error: " + "bitCount != (redBitCount + greenBitCount + blueBitCount)");
+ }
this.bitCount = bitCount;
+ this.redBitCount = redBitCount;
+ this.greenBitCount = greenBitCount;
+ this.blueBitCount = blueBitCount;
+
+ }
+ ColorDepth(int bitCount) {
+ this(bitCount, 0, 0, 0);
}
public static ColorDepth from(int bitCount) {
switch (bitCount) {
diff --git a/src/main/java/com/pixelgamelibrary/api/graphics/ColorDepthHelper.java b/src/main/java/com/pixelgamelibrary/api/graphics/ColorDepthHelper.java
new file mode 100644
index 0000000..191395e
--- /dev/null
+++ b/src/main/java/com/pixelgamelibrary/api/graphics/ColorDepthHelper.java
@@ -0,0 +1,66 @@
+///////////////////////////////////////////////////////////////////////////////////////////////
+// Pixel: Game library.
+// Copyright (C) 2024 the original author or authors.
+//
+// This program is free software: you can redistribute it and/or
+// modify it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see
+// or write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+///////////////////////////////////////////////////////////////////////////////////////////////
+package com.pixelgamelibrary.api.graphics;
+
+import com.pixelgamelibrary.api.Pixel;
+import com.pixelgamelibrary.api.utils.BinaryUtils;
+import java.util.BitSet;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ *
+ * @author robertvokac
+ */
+@AllArgsConstructor
+@Getter
+public class ColorDepthHelper {
+
+ final int r, g, b;
+ final int redBits, greenBits, blueBits;
+
+ public ColorDepthHelper(Color color, ColorDepth colorDepth) {
+ this(color, colorDepth.getRedBitCount(), colorDepth.getGreenBitCount(), colorDepth.getBlueBitCount());
+ }
+ public ColorDepthHelper(Color color, int redBits, int greenBits, int blueBits) {
+ // Create masks based on the number of bits for each color component
+ int redMask = (1 << redBits) - 1; // Mask for red
+ int greenMask = (1 << greenBits) - 1; // Mask for green
+ int blueMask = (1 << blueBits) - 1; // Mask for blue
+
+ // Reduce each color component to the specified number of bits and apply the mask
+ this.r = (Color.floatElementToInt(color.getRed()) >> (BinaryUtils.BITS_PER_BYTE - redBits)) & redMask;
+ this.g = (Color.floatElementToInt(color.getGreen()) >> (BinaryUtils.BITS_PER_BYTE - greenBits)) & greenMask;
+ this.b = (Color.floatElementToInt(color.getBlue()) >> (BinaryUtils.BITS_PER_BYTE - blueBits)) & blueMask;
+ this.redBits = redBits;
+ this.greenBits = greenBits;
+ this.blueBits = blueBits;
+ }
+ public BitSet getBitSet() {
+ BinaryUtils bu = Pixel.utils().binary();
+ return bu.merge3BitSets(
+ bu.convertIntToBitSet(r, redBits),
+ bu.convertIntToBitSet(g, greenBits),
+ bu.convertIntToBitSet(b, blueBits),
+ redBits,
+ greenBits,
+ blueBits);
+ }
+}
diff --git a/src/main/java/com/pixelgamelibrary/api/interfaces/Utils.java b/src/main/java/com/pixelgamelibrary/api/interfaces/Utils.java
index b8a60fd..d29a278 100644
--- a/src/main/java/com/pixelgamelibrary/api/interfaces/Utils.java
+++ b/src/main/java/com/pixelgamelibrary/api/interfaces/Utils.java
@@ -19,6 +19,7 @@
///////////////////////////////////////////////////////////////////////////////////////////////
package com.pixelgamelibrary.api.interfaces;
+import com.pixelgamelibrary.api.utils.BinaryUtilsImpl;
import com.pixelgamelibrary.api.utils.CollectionUtils;
import com.pixelgamelibrary.api.utils.ReflectionUtils;
import java.util.Arrays;
@@ -57,5 +58,8 @@ public interface Utils {
}
ReflectionUtils reflection();
CollectionUtils collections();
+ default BinaryUtilsImpl binary() {
+ return BinaryUtilsImpl.INSTANCE;
+ }
}
diff --git a/src/main/java/com/pixelgamelibrary/api/utils/BinaryUtils.java b/src/main/java/com/pixelgamelibrary/api/utils/BinaryUtils.java
new file mode 100644
index 0000000..3e50eb1
--- /dev/null
+++ b/src/main/java/com/pixelgamelibrary/api/utils/BinaryUtils.java
@@ -0,0 +1,33 @@
+///////////////////////////////////////////////////////////////////////////////////////////////
+// Pixel: Game library.
+// Copyright (C) 2024 the original author or authors.
+//
+// This program is free software: you can redistribute it and/or
+// modify it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see
+// or write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+package com.pixelgamelibrary.api.utils;
+
+import java.util.BitSet;
+
+/**
+ *
+ * @author robertvokac
+ */
+public interface BinaryUtils {
+ public int BITS_PER_BYTE = 8;
+ BitSet convertIntToBitSet(int value, int bitCount);
+ BitSet merge3BitSets(BitSet bitSet1, BitSet bitSet2, BitSet bitSet3, int bitSet1Size, int bitSet2Size, int bitSet3Size);
+}
diff --git a/src/main/java/com/pixelgamelibrary/api/utils/BinaryUtilsImpl.java b/src/main/java/com/pixelgamelibrary/api/utils/BinaryUtilsImpl.java
new file mode 100644
index 0000000..9b0aa1c
--- /dev/null
+++ b/src/main/java/com/pixelgamelibrary/api/utils/BinaryUtilsImpl.java
@@ -0,0 +1,82 @@
+///////////////////////////////////////////////////////////////////////////////////////////////
+// Pixel: Game library.
+// Copyright (C) 2024 the original author or authors.
+//
+// This program is free software: you can redistribute it and/or
+// modify it under the terms of the GNU 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see
+// or write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+///////////////////////////////////////////////////////////////////////////////////////////////
+package com.pixelgamelibrary.api.utils;
+
+import java.util.BitSet;
+
+/**
+ *
+ * @author robertvokac
+ */
+public class BinaryUtilsImpl implements BinaryUtils {
+
+ public static final BinaryUtilsImpl INSTANCE = new BinaryUtilsImpl();
+ private BinaryUtilsImpl() {
+
+ }
+ public BitSet convertIntToBitSet(int value, int bitCount) {
+ BitSet bitSet = new BitSet(bitCount);
+
+ int index = 0;
+
+ while (value != 0) {
+ if ((value & 1) != 0) {
+ bitSet.set(index);
+ }
+ value >>= 1;
+ index++;
+ }
+ return bitSet;
+ }
+ public BitSet merge3BitSets(BitSet bitSet1, BitSet bitSet2, BitSet bitSet3, int bitSet1Size, int bitSet2Size, int bitSet3Size) {
+ // Create a new BitSet with a size corresponding to the sum of the sizes of all input BitSets
+ int totalSize = bitSet1Size + bitSet2Size + bitSet3Size;
+ BitSet result = new BitSet(totalSize);
+
+ int currentIndex = 0; // Current index for the output BitSet
+
+ // Add bits from the first BitSet
+ for (int i = 0; i < bitSet1Size; i++) {
+ if (bitSet1.get(i)) {
+ result.set(currentIndex);
+ }
+ currentIndex++;
+ }
+
+ // Add bits from the second BitSet
+ for (int i = 0; i < bitSet2Size; i++) {
+ if (bitSet2.get(i)) {
+ result.set(currentIndex);
+ }
+ currentIndex++;
+ }
+
+ // Add bits from the third BitSet
+ for (int i = 0; i < bitSet3Size; i++) {
+ if (bitSet3.get(i)) {
+ result.set(currentIndex);
+ }
+ currentIndex++;
+ }
+
+ return result;
+ }
+
+}