package Pixels;
public class BufferConverter
{
static final int TYPE_NONE = 0;
static final int TYPE_BIT = 1;
static final int TYPE_BYTE = 2;
static final int TYPE_SHORT = 3;
static final int TYPE_FLOAT = 4;
public static int convertToPackedArgb(
int sampleType,
boolean isLittleEndian,
boolean isPlanar,
boolean shouldDiffOrInvert,
int[] pixels,
byte[] imageBuffer,
int size,
int width,
int height,
int channels,
float maxval
) {
if (shouldDiffOrInvert && sampleType != TYPE_BIT) {
int sampleGap;
int nRows;
if (isPlanar) {
sampleGap = 1;
nRows = height * channels;
}
else {
sampleGap = channels;
nRows = height;
}
if (sampleType == TYPE_BYTE)
diffSamplesHorizontallyAsBytes(imageBuffer, size, width, nRows, sampleGap);
else if (sampleType == TYPE_SHORT)
diffSamplesHorizontallyAsShorts(imageBuffer, size, width, nRows, sampleGap, isLittleEndian);
else
diffSamplesHorizontallyAsFloats(imageBuffer, size, width, nRows, sampleGap, isLittleEndian);
}
if (isPlanar) {
if (sampleType == TYPE_BIT)
return getPackedArgbFromBitsPlanar(pixels, imageBuffer, size, width, height, channels, shouldDiffOrInvert);
else if (sampleType == TYPE_BYTE)
return getPackedArgbFromBytesPlanar(pixels, imageBuffer, size, width, height, channels, (int)maxval);
else if (sampleType == TYPE_SHORT)
return getPackedArgbFromShortsPlanar(pixels, imageBuffer, size, width, height, channels, (int)maxval, isLittleEndian);
else if (sampleType == TYPE_FLOAT)
return getPackedArgbFromFloatsPlanar(pixels, imageBuffer, size, width, height, channels, maxval, isLittleEndian);
}
else {
if (sampleType == TYPE_BIT)
return getPackedArgbFromBits(pixels, imageBuffer, size, width, height, channels, shouldDiffOrInvert);
else if (sampleType == TYPE_BYTE)
return getPackedArgbFromBytes(pixels, imageBuffer, size, width, height, channels, (int)maxval);
else if (sampleType == TYPE_SHORT)
return getPackedArgbFromShorts(pixels, imageBuffer, size, width, height, channels, (int)maxval, isLittleEndian);
else if (sampleType == TYPE_FLOAT)
return getPackedArgbFromFloats(pixels, imageBuffer, size, width, height, channels, maxval, isLittleEndian);
}
return 0;
}
public static void diffSamplesHorizontallyAsBytes(
byte[] imageBuffer,
int size,
int width,
int nRows,
int sampleGap
) {
int stride = sampleGap * width;
if (stride <= 0)
return;
int rows = Math.min(nRows, size / stride);
for (int y = 0; y < rows; y++) {
for (int p = sampleGap; p < stride; p++)
imageBuffer[p+stride*y] += imageBuffer[p-sampleGap+stride*y];
}
}
public static void diffSamplesHorizontallyAsShorts(
byte[] imageBuffer,
int size,
int width,
int nRows,
int sampleGap,
boolean isLittleEndian
) {
int endian = isLittleEndian ? 1 : 0;
int bytesPerPixel = 2 * sampleGap;
int stride = bytesPerPixel * width;
if (stride <= 0)
return;
int rows = Math.min(nRows, size / stride);
for (int y = 0; y < rows; y++) {
for (int p = bytesPerPixel; p < stride; p += 2) {
short cur = (short)(
(imageBuffer[p+stride*y + endian] & 0xff) << 8 |
(imageBuffer[p+stride*y + (endian^1)] & 0xff)
);
short prev = (short)(
(imageBuffer[p-bytesPerPixel+stride*y + endian] & 0xff) << 8 |
(imageBuffer[p-bytesPerPixel+stride*y + (endian^1)] & 0xff)
);
cur += prev;
imageBuffer[p+stride*y + endian] = (byte)(cur >> 8);
imageBuffer[p+stride*y + (endian^1)] = (byte)cur;
}
}
}
public static void diffSamplesHorizontallyAsFloats(
byte[] imageBuffer,
int size,
int width,
int nRows,
int sampleGap,
boolean isLittleEndian
) {
int s0, s1, s2, s3;
if (isLittleEndian) {
s0 = 0;
s1 = 8;
s2 = 16;
s3 = 24;
}
else {
s0 = 24;
s1 = 16;
s2 = 8;
s3 = 0;
}
int bytesPerPixel = 4 * sampleGap;
int stride = bytesPerPixel * width;
if (stride <= 0)
return;
int rows = Math.min(nRows, size / stride);
for (int y = 0; y < rows; y++) {
for (int p = bytesPerPixel; p < stride; p += 4) {
int pos = p+stride*y;
float cur = Float.intBitsToFloat(
(imageBuffer[pos] & 0xff) << s0 |
(imageBuffer[pos+1] & 0xff) << s1 |
(imageBuffer[pos+2] & 0xff) << s2 |
(imageBuffer[pos+3] & 0xff) << s3
);
float prev = Float.intBitsToFloat(
(imageBuffer[pos-bytesPerPixel] & 0xff) << s0 |
(imageBuffer[pos-bytesPerPixel+1] & 0xff) << s1 |
(imageBuffer[pos-bytesPerPixel+2] & 0xff) << s2 |
(imageBuffer[pos-bytesPerPixel+3] & 0xff) << s3
);
int bits = Float.floatToRawIntBits(cur + prev);
imageBuffer[pos] = (byte)(bits >> s0);
imageBuffer[pos+1] = (byte)(bits >> s1);
imageBuffer[pos+2] = (byte)(bits >> s2);
imageBuffer[pos+3] = (byte)(bits >> s3);
}
}
}
public static int getPackedArgbFromBits(
int[] pixels,
byte[] imageBuffer,
int size,
int width,
int height,
int channels,
boolean shouldInvert
) {
int xorValue = shouldInvert ? (1 << 24) - 1 : 0;
int area = width * height;
int trueSize = Math.min(size, Math.max(channels, 1) * (area + 7) / 8);
if (channels <= 1) {
for (int i = 0; i < trueSize; i++) {
byte c = imageBuffer[i];
for (int j = 0; j < 8 && i*8+j < area; j++)
pixels[i*8+j] = 0xff000000 | (xorValue ^ -((c >> (7-j)) & 1));
}
}
else if (channels <= 3) {
int ch = 0;
int idx = 0;
int argb = 0xff000000;
for (int i = 0; i < trueSize; i++) {
byte c = imageBuffer[i];
for (int j = 0; j < 8; j++) {
argb = (argb << 8) | (xorValue ^ -((c >> (7-j)) & 1));
if (++ch >= channels) {
ch = 0;
pixels[idx++] = argb;
if (idx >= area)
break;
argb = 0xff000000;
}
}
}
}
else {
int ch = 0;
int idx = 0;
int argb = 0;
for (int i = 0; i < trueSize; i++) {
byte c = imageBuffer[i];
for (int j = 0; j < 8; j++) {
if (ch < 4)
argb = (argb << 8) | (xorValue ^ -((c >> (7-j)) & 1));
if (++ch >= channels) {
ch = 0;
pixels[idx++] = argb;
if (idx >= area)
break;
}
}
}
}
return Math.min(area, (size * 8) / Math.max(channels, 1));
}
public static int getPackedArgbFromBitsPlanar(
int[] pixels,
byte[] imageBuffer,
int size,
int width,
int height,
int channels,
boolean shouldInvert
) {
int xorValue = shouldInvert ? (1 << 24) - 1 : 0;
int area = width * height;
int planeSize = Math.min(size, (area + 7) / 8);
for (int i = 0; i < planeSize; i++) {
byte v = imageBuffer[i];
for (int j = 0; j < 8 && i*8+j < area; j++)
pixels[i*8+j] = 0xff000000 | (xorValue ^ -((v >> (7-j)) & 1));
}
for (int ch = 1; ch < channels; ch++) {
int mask = 0xff << (8*ch);
for (int i = 0; i < planeSize && ch*planeSize+i < size; i++) {
byte v = imageBuffer[ch*planeSize+i];
for (int j = 0; j < 8 && i*8+j < area; j++)
pixels[i*8+j] = (pixels[i*8+j] & ~mask) | ((xorValue ^ -((v >> (7-j)) & 1)) & mask);
}
}
return Math.min(area, size * 8);
}
public static int getPackedArgbFromBytes(
int[] pixels,
byte[] imageBuffer,
int size,
int width,
int height,
int channels,
int maxval
) {
maxval = maxval <= 0 ? 255 : maxval;
int area = width * height;
if (channels <= 1) {
int len = Math.min(size, area);
if (maxval == 255) {
for (int i = 0; i < len; i++) {
int lum = imageBuffer[i] & 0xff;
pixels[i] = 0xff000000 | (lum << 16) | (lum << 8) | lum;
}
}
else {
int factor = 256 / (maxval + 1);
for (int i = 0; i < len; i++) {
int lum = Math.min(Math.max(factor * (imageBuffer[i] & 0xff), 0), 255);
pixels[i] = 0xff000000 | (lum << 16) | (lum << 8) | lum;
}
}
}
else if (channels == 2) {
int len = Math.min(size, area * 2);
if (maxval == 255) {
for (int in = 0, out = 0; in < len; in += 2, out++) {
int a = imageBuffer[in] & 0xff;
int b = imageBuffer[in+1] & 0xff;
pixels[out] = 0xff000000 | (a << 8) | b;
}
}
else {
int factor = 256 / (maxval + 1);
for (int in = 0, out = 0; in < len; in += 2, out++) {
int a = Math.min(Math.max(factor * (imageBuffer[in] & 0xff), 0), 255);
int b = Math.min(Math.max(factor * (imageBuffer[in+1] & 0xff), 0), 255);
pixels[out] = 0xff000000 | (a << 8) | b;
}
}
}
else if (channels == 3) {
int len = Math.min(size, area * 3);
if (maxval == 255) {
for (int in = 0, out = 0; in < len; in += 3, out++) {
int r = imageBuffer[in] & 0xff;
int g = imageBuffer[in+1] & 0xff;
int b = imageBuffer[in+2] & 0xff;
pixels[out] = 0xff000000 | (r << 16) | (g << 8) | b;
}
}
else {
int factor = 256 / (maxval + 1);
for (int in = 0, out = 0; in < len; in += 3, out++) {
int r = Math.min(Math.max(factor * (imageBuffer[in] & 0xff), 0), 255);
int g = Math.min(Math.max(factor * (imageBuffer[in+1] & 0xff), 0), 255);
int b = Math.min(Math.max(factor * (imageBuffer[in+2] & 0xff), 0), 255);
pixels[out] = 0xff000000 | (r << 16) | (g << 8) | b;
}
}
}
else if (channels == 4) {
int len = Math.min(size, area * 4);
if (maxval == 255) {
for (int in = 0, out = 0; in < len; in += 4, out++) {
int a = imageBuffer[in] & 0xff;
int r = imageBuffer[in+1] & 0xff;
int g = imageBuffer[in+2] & 0xff;
int b = imageBuffer[in+3] & 0xff;
pixels[out] = (a << 24) | (r << 16) | (g << 8) | b;
}
}
else {
int factor = 256 / (maxval + 1);
for (int in = 0, out = 0; in < len; in += 4, out++) {
int a = Math.min(Math.max(factor * (imageBuffer[in] & 0xff), 0), 255);
int r = Math.min(Math.max(factor * (imageBuffer[in+1] & 0xff), 0), 255);
int g = Math.min(Math.max(factor * (imageBuffer[in+2] & 0xff), 0), 255);
int b = Math.min(Math.max(factor * (imageBuffer[in+3] & 0xff), 0), 255);
pixels[out] = (a << 24) | (r << 16) | (g << 8) | b;
}
}
}
else {
int argb = 0xff000000;
int ch = 0;
int idx = 0;
if (maxval == 255) {
for (int i = 0; i < size; i++) {
if (ch < 4) {
int lum = imageBuffer[i] & 0xff;
argb = (argb << 8) | lum;
}
if (++ch >= channels) {
ch = 0;
pixels[idx++] = argb;
if (idx >= area)
break;
argb = 0xff000000;
}
}
}
else {
int factor = 256 / (maxval + 1);
for (int i = 0; i < size; i++) {
if (ch < 4) {
int lum = Math.min(Math.max(factor * (imageBuffer[i] & 0xff), 0), 255);
argb = (argb << 8) | lum;
}
if (++ch >= channels) {
ch = 0;
pixels[idx++] = argb;
if (idx >= area)
break;
argb = 0xff000000;
}
}
}
}
return Math.min(area, size / Math.min(Math.max(channels, 1), 4));
}
public static int getPackedArgbFromBytesPlanar(
int[] pixels,
byte[] imageBuffer,
int size,
int width,
int height,
int channels,
int maxval
) {
maxval = maxval <= 0 ? 255 : maxval;
int area = width * height;
int len = Math.min(size, area);
if (maxval == 255) {
for (int i = 0; i < len; i++) {
int lum = imageBuffer[i] & 0xff;
pixels[i] = 0xff000000 | (lum << 16) | (lum << 8) | lum;
}
for (int ch = 1; ch < channels; ch++) {
len = Math.min(size - ch*area, area);
int mask = ~(0xff << (8*ch));
for (int i = 0; i < len; i++) {
int lum = imageBuffer[ch*area+i] & 0xff;
pixels[i] = (pixels[i] & mask) | (lum << (8*ch));
}
}
}
else {
int factor = 256 / (maxval + 1);
for (int i = 0; i < len; i++) {
int lum = Math.min(Math.max(factor * (imageBuffer[i] & 0xff), 0), 255);
pixels[i] = 0xff000000 | (lum << 16) | (lum << 8) | lum;
}
for (int ch = 1; ch < channels; ch++) {
len = Math.min(size - ch*area, area);
int mask = ~(0xff << (8*ch));
for (int i = 0; i < len; i++) {
int lum = Math.min(Math.max(factor * (imageBuffer[ch*area+i] & 0xff), 0), 255);
pixels[i] = (pixels[i] & mask) | (lum << (8*ch));
}
}
}
return Math.min(size, area);
}
public static int getPackedArgbFromShorts(
int[] pixels,
byte[] imageBuffer,
int size,
int width,
int height,
int channels,
int maxval,
boolean isLittleEndian
) {
maxval = Math.max(maxval <= 0 ? 65535 : maxval, 255);
int endian = isLittleEndian ? 1 : 0;
int area = width * height;
if (channels <= 1) {
int len = Math.min(size / 2, area);
if (maxval == 65535) {
for (int i = 0; i < len; i++) {
int lum = imageBuffer[2*i+endian] & 0xff;
pixels[i] = 0xff000000 | (lum << 16) | (lum << 8) | lum;
}
}
else {
int factor = (maxval + 1) / 256;
for (int i = 0; i < len; i++) {
int value = (imageBuffer[2*i+endian] & 0xff) << 8 | (imageBuffer[2*i+(endian^1)] & 0xff);
int lum = Math.min(Math.max(value / factor, 0), 255);
pixels[i] = 0xff000000 | (lum << 16) | (lum << 8) | lum;
}
}
}
else {
int argb = 0xff000000;
int ch = 0;
int idx = 0;
if (maxval == 65535) {
for (int i = 0; i < size; i += 2) {
if (ch < 4) {
int lum = imageBuffer[i+endian] & 0xff;
argb = (argb << 8) | lum;
}
if (++ch >= channels) {
ch = 0;
pixels[idx++] = argb;
if (idx >= area)
break;
argb = 0xff000000;
}
}
}
else {
int factor = (maxval + 1) / 256;
for (int i = 0; i < size; i += 2) {
if (ch < 4) {
int value = (imageBuffer[i+endian] & 0xff) << 8 | (imageBuffer[i+(endian^1)] & 0xff);
int lum = Math.min(Math.max(value / factor, 0), 255);
argb = (argb << 8) | lum;
}
if (++ch >= channels) {
ch = 0;
pixels[idx++] = argb;
if (idx >= area)
break;
argb = 0xff000000;
}
}
}
}
return Math.min(area, size / (2 * Math.min(Math.max(channels, 1), 4)));
}
public static int getPackedArgbFromShortsPlanar(
int[] pixels,
byte[] imageBuffer,
int size,
int width,
int height,
int channels,
int maxval,
boolean isLittleEndian
) {
maxval = Math.max(maxval <= 0 ? 65535 : maxval, 255);
int endian = isLittleEndian ? 1 : 0;
int area = width * height;
int len = Math.min(size / 2, area);
if (maxval == 65535) {
for (int i = 0; i < len; i++) {
int lum = imageBuffer[2*i+endian] & 0xff;
pixels[i] = 0xff000000 | (lum << 16) | (lum << 8) | lum;
}
for (int ch = 1; ch < channels; ch++) {
len = Math.min(size / 2 - ch*area, area);
int mask = ~(0xff << (8*ch));
for (int i = 0; i < len; i++) {
int lum = imageBuffer[2*(ch*area+i) + endian] & 0xff;
pixels[i] = (pixels[i] & mask) | (lum << (8*ch));
}
}
}
else {
int factor = (maxval + 1) / 256;
for (int in = 0, out = 0; out < len; in += 2, out++) {
int value = (imageBuffer[in+endian] & 0xff) << 8 | (imageBuffer[in+(endian^1)] & 0xff);
int lum = Math.min(Math.max(value / factor, 0), 255);
pixels[out] = 0xff000000 | (lum << 16) | (lum << 8) | lum;
}
for (int ch = 1; ch < channels; ch++) {
len = Math.min(size / 2 - ch*area, area);
int mask = ~(0xff << (8*ch));
for (int in = 2*ch*area, out = 0; out < len; in += 2, out++) {
int value = (imageBuffer[in+endian] & 0xff) << 8 | (imageBuffer[in+(endian^1)] & 0xff);
int lum = Math.min(Math.max(value / factor, 0), 255);
pixels[out] = (pixels[out] & mask) | (lum << (8*ch));
}
}
}
return Math.min(size / 2, area);
}
public static int getPackedArgbFromFloats(
int[] pixels,
byte[] imageBuffer,
int size,
int width,
int height,
int channels,
double maxval,
boolean isLittleEndian
) {
int s0, s1, s2, s3;
if (isLittleEndian) {
s0 = 0;
s1 = 8;
s2 = 16;
s3 = 24;
}
else {
s0 = 24;
s1 = 16;
s2 = 8;
s3 = 0;
}
maxval = maxval <= 0.0 ? 1.0 : maxval;
float factor = 256.0f / (float)maxval;
int area = width * height;
if (channels <= 1) {
int len = Math.min(size / 4, area);
for (int i = 0; i < len; i++) {
float value = Float.intBitsToFloat(
(imageBuffer[4*i] & 0xff) << s0 |
(imageBuffer[4*i+1] & 0xff) << s1 |
(imageBuffer[4*i+2] & 0xff) << s2 |
(imageBuffer[4*i+3] & 0xff) << s3
);
int lum = Math.min(Math.max((int)(factor * value), 0), 255);
pixels[i] = 0xff000000 | (lum << 16) | (lum << 8) | lum;
}
}
else {
int argb = 0xff000000;
int ch = 0;
int idx = 0;
for (int i = 0; i < size; i += 4) {
if (ch < 4) {
float value = Float.intBitsToFloat(
(imageBuffer[4*i] & 0xff) << s0 |
(imageBuffer[4*i+1] & 0xff) << s1 |
(imageBuffer[4*i+2] & 0xff) << s2 |
(imageBuffer[4*i+3] & 0xff) << s3
);
int lum = Math.min(Math.max((int)(factor * value), 0), 255);
argb = (argb << 8) | lum;
}
if (++ch >= channels) {
ch = 0;
pixels[idx++] = argb;
if (idx >= area)
break;
argb = 0xff000000;
}
}
}
return Math.min(area, size / (4 * Math.min(Math.max(channels, 1), 4)));
}
public static int getPackedArgbFromFloatsPlanar(
int[] pixels,
byte[] imageBuffer,
int size,
int width,
int height,
int channels,
double maxval,
boolean isLittleEndian
) {
int s0, s1, s2, s3;
if (isLittleEndian) {
s0 = 0;
s1 = 8;
s2 = 16;
s3 = 24;
}
else {
s0 = 24;
s1 = 16;
s2 = 8;
s3 = 0;
}
maxval = maxval <= 0.0 ? 1.0 : maxval;
float factor = 256.0f / (float)maxval;
int area = width * height;
int len = Math.min(size / 4, area);
for (int in = 0, out = 0; out < len; in += 4, out++) {
float value = Float.intBitsToFloat(
(imageBuffer[in] & 0xff) << s0 |
(imageBuffer[in+1] & 0xff) << s1 |
(imageBuffer[in+2] & 0xff) << s2 |
(imageBuffer[in+3] & 0xff) << s3
);
int lum = Math.min(Math.max((int)(factor * value), 0), 255);
pixels[out] = 0xff000000 | (lum << 16) | (lum << 8) | lum;
}
for (int ch = 1; ch < channels; ch++) {
len = Math.min(size / 2 - ch*area, area);
int mask = ~(0xff << (8*ch));
for (int in = 4*ch*area, out = 0; out < len; in += 4, out++) {
float value = Float.intBitsToFloat(
(imageBuffer[in] & 0xff) << s0 |
(imageBuffer[in+1] & 0xff) << s1 |
(imageBuffer[in+2] & 0xff) << s2 |
(imageBuffer[in+3] & 0xff) << s3
);
int lum = Math.min(Math.max((int)(factor * value), 0), 255);
pixels[out] = (pixels[out] & mask) | (lum << (8*ch));
}
}
return Math.min(size / 4, area);
}
}