Added new class Color IV
This commit is contained in:
parent
b1c3ff2d28
commit
7d39047402
@ -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
|
||||
|
@ -18,7 +18,7 @@ package com.pixelgamelibrary.api.graphics;
|
||||
// along with this program. If not, see
|
||||
// <https://www.gnu.org/licenses/> 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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
// <https://www.gnu.org/licenses/> 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
// <https://www.gnu.org/licenses/> 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);
|
||||
}
|
@ -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
|
||||
// <https://www.gnu.org/licenses/> 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;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user