I've got this loop that is run thousands of times (so needs to be efficient). It changes the value of the bitmap pixel.
I want to be able to run thought the loop and "switch" a certain group of pixels to alpha and then switch them back at a later point.
My question is.
How do I switch the values? So say 0xFFCC1BE0 becomes 0x00CC1BE0 and then if I want to switch back to 0xFFCC1BE0 I simply take the 00 and turn it too FF.
I can't make two bitmaps as I run out of memory :-(
Anyhow here's what I've got so far:
private void setTransparencyOnLightMap(float WidthPercentage, float LeftPosition)
{
int blankPixel = 0x00000000;
int savedPixel = 0x00000000;
int desiredAlpha = 200; //Can also be 0x00
//Find away of turning alhpa off and on.
for(int BMx = 0; BMx < mLightMap.getWidth(); BMx++)
{
for(int BMy = 0; BMy < mLightMap.getHeight(); BMy++)
{
if(mLightMap.getPixel(BMx, BMy) != blankPixel) //Make sure don't overwrite blank transparent pixels.
{
savedPixel = mLightMap.getPixel(BMx,BMy);
savedPixel = savedPixel | (desiredAlpha << 24);
mLightMap.setPixel(BMx, BMy, savedPixel);
}
}
}
}
You could switch the alpha of a pixel like so:
savedPixel = savedPixel & 0x00FFFFFF;
savedPixel = savedPixel | (desiredAlpha << 24);
The first line zeros out the 8 most significant bits of savedPixel (these are the bits where the alpha is held). The second line sets the 8 most significant bits of savedPixel to desiredAlpha. Note that desiredAlpha must be between 0 and 255 (These are the ints that can be stored in 8 bits).
Note that this uses bitwise operators, (&, |, <<) which are very efficient.
It seems to me, to reduce memory use, you can just save the original Alpha value of each pixel rather than the whole ARGB value - to do this use a byte array which will be 1/4 the size of the original bitmap. Also use a bit-mask for the new Alpha so you can use bitwise AND (&) as described by Tristan Hull...
byte[] savedAlphaArray = new byte[mLightMap.getWidth(), mLightMap.getHeight()];
int desiredAlphaMask = 0x00FFFFFF;
int pixel;
Then to save the Alpha values and apply the bit-mask do the following...
for (int i = 0; i < mLightMap.getWidth(); i++) {
for (int j = 0; j < mLightMap.getHeight(); j++) {
pixel = mLightMap.getPixel(i, j);
savedAlphaArray[i, j] = (pixel & 0xFF000000) >> 24;
mLightMap.setPixel(i, j, desiredAlphaMask & pixel);
}
}
To 'switch' back do the following...
for (int i = 0; i < mLightMap.getWidth(); i++) {
for (int j = 0; j < mLightMap.getHeight(); j++) {
pixel = mLightMap.getPixel(i, j);
mLightMap.setPixel(i, j, savedAlphaArray[i, j] << 24 & pixel);
}
}
Related
I'm using the following loop to calculate the difference between two images of the same size:
static double calculateError(BufferedImage canvas, BufferedImage ideal) {
double error = 0.0D;
for (int x = 0; x < Polygonizer.WIDTH; x++) {
for (int y = 0; y < Polygonizer.HEIGHT; y++) {
Color c1 = new Color(canvas.getRGB(x, y));
Color c2 = new Color(ideal.getRGB(x, y));
error += Math.abs(c1.getRed() - c2.getRed());
error += Math.abs(c1.getGreen() - c2.getGreen());
error += Math.abs(c1.getBlue() - c2.getBlue());
}
}
return error;
}
It works fine but rather slow, and I have no idea how to make it go faster.
I've already tried using ExecutorService to no good and I could use some advice.
Edit: Thanks, everyone. Here's the optimized version:
private static int APPROXIMATION = 1;
private static int[] idealData;
public static final void setIdeal(BufferedImage ideal) {
int[] rawData = ((DataBufferInt)ideal.getRaster().getDataBuffer()).getData();
idealData = new int[rawData.length * 3];
int counter = 0;
for (int i = 0; i < rawData.length * 3; i += 3) {
idealData[i] = (rawData[counter] & 0xFF);
idealData[i + 1] = (rawData[counter] >> 8 & 0xFF);
idealData[i + 2] = (rawData[counter] >> 16 & 0xFF);
counter++;
}
}
static double calculateError(BufferedImage canvas) {
long error = 0;
final int[] canvasData = ((DataBufferInt)canvas.getRaster().getDataBuffer()).getData();
int counter = 0;
for (int i = 0; i < canvasData.length; i += APPROXIMATION) {
error += Math.abs((canvasData[i] & 0xFF) - (idealData[counter]));
error += Math.abs((canvasData[i] >> 8 & 0xFF) - (idealData[counter + 1]));
error += Math.abs((canvasData[i] >> 16 & 0xFF) - (idealData[counter + 2]));
counter += 3 * APPROXIMATION;
}
return error;
}
If you know the format of the BufferedImages, for example because you instantiated them with the constructor, there's an even faster way.
If the BufferedImages are of type TYPE_3BYTE_BGR, do this:
byte[] canvasData = ((DataBufferByte)canvas.getRaster().getDataBuffer()).getData();
byte[] idealData = ((DataBufferByte)ideal .getRaster().getDataBuffer()).getData();
int error = 0;
for (int i = 0; i < canvasData.length; i++) {
error += Math.abs((canvasData[i] & 0xFF) - (idealData[i] & 0xFF));
}
If the BufferedImages are of type TYPE_INT_RGB, do this:
int[] canvasData = ((DataBufferInt)canvas.getRaster().getDataBuffer()).getData();
int[] idealData = ((DataBufferInt)ideal .getRaster().getDataBuffer()).getData();
int error = 0;
for (int i = 0; i < canvasData.length; i++) {
error += Math.abs((canvasData[i] & 0xFF) - (idealData[i] & 0xFF));
error += Math.abs((canvasData[i] >> 8 & 0xFF) - (idealData[i] >> 8 & 0xFF));
error += Math.abs((canvasData[i] >> 16 & 0xFF) - (idealData[i] >> 16 & 0xFF));
}
(disclaimer: Untested code, but something I've done often enough that I'm confident)
You create two instances of Color per point, that's quite a bit. There's a much faster way of extracting the RGB from an int (look at the sources of Color).
More importantly, you extract points one by one, which is usually terribly slow (as they may be organized differently in the BufferedImages). Call a method extracting more of them at once into an int[].
AFAIK there's a way to let the image compute the result, which is probably still faster.
I've already tried using ExecutorService to no good and I could really use some advice.
This should also help, but only up to the number of cores you have. However, using paralel streams is usually much simpler. But that's a different story (question).
In my project I need to open the SEQ file, so I use FileInputStream, it requires to load data to byte array. But because of that each pixels get wrong value (cause they are Integers).
Below in my code you can see that i put pixels in 2d array and for that I count each value of pixel, in line:
wart =(int) (buf[offset]) +(int)(buf[offset+1]) * 255;
I know that values because of byte input format are wrong (first two pixels aka double should be 152,109692453756 and 152,068644316116 but in my Java function they get -2474, -690)
I tried using the mask:
wart =(int) (buf[offset]<< 8) & 0x0000ff00 +(int)(buf[offset+1])& 0x000000ff * 255 ;
it helps a little (values arent negative, but they are "shifted" too much (first two pixels 19456, 18944)
I don't know how to solve this problem. I know that the mask should be different, but I don't know how to set it.
public class Sekwencja2 {
#SuppressWarnings("empty-statement")
public double[] sekwencja2(String nazwa,int nr_klatki) throws FileNotFoundException, IOException{
InputStream is = null;
DataInputStream dis = null;
is = new FileInputStream(nazwa);
dis = new DataInputStream(is);
int length = dis.available();
byte[] buf = new byte[length];
dis.readFully(buf);
int l_klatek = ((length-158864)/158864)+1;
int width = 320;
int height = 240;
int C1=21764040;
double C2=3033.3;
double C3=134.06;
int z = 0;
double[] oneDArray = new double[width*height];
double [][] pixels = new double[width][height];
int offset =0;
char type;
String typeText;
type=(char)buf[0];
typeText =Character.toString(type);
switch (typeText) {
case "A":
if(nr_klatki == 1)
offset= 696;
else
offset = 158136+(nr_klatki-1)*569+(nr_klatki-2)*(320*240*2+3839);
break;
case "F":
offset=(nr_klatki-1)*158864 + 1373;
break;
}
int wart = 0 ;
for(int x = 0; x<320; x++){
for (int y = 0; y<240;y++){
switch (typeText){
case "A":
if(nr_klatki==1)
wart =(int) (buf[offset]) +(int)(buf[offset+1]) * 255;
else
wart = (int)(buf[offset]<< 8)& 0x0000ff00 +(int)(buf[offset+1])&0xff*255 ;
break;
case "F":
wart = (buf[offset]<< 8)& 0x0000ff00 +(buf[offset+1])& 0x000000ff * 255 ;
break;
}
System.out.print(", "+wart);
pixels[x][y]=wart;
offset = offset+2;
}
}
for(int i = 0; i < width; i ++)
{
System.arraycopy(pixels[i], 0, oneDArray, i * height, height);
}
return oneDArray;
}
}
I know it's a mess, a lot of things are commented :)
255 is wrong
it's 256, you always have to multiply by a multiple of the base you are operating, 255 isn't a multiple of 2
analogy:
convert 111 from base 10 to base 10 in your way
1*99 + 1*9 + 1 = 109
so 109 != 111 which is wrong, likewise multiplying by 255 will alter any number you try to convert from binary to binary.
Mask first, like this:
wart = (buf[offset] & 0xFF) | ((buf[offset+1] & 0xFF) << 8);
I can't print my bit image without white line each 24 lines. I need to print an image, but this image have white lines each 24 lines to bits.
The epson printer print the image to the next format.
>line 23 01010101000010001010
>line 24 00001000100000000110
>line 25 --------- white line ------------
How can delete this damn white line?
Image size
width:400px
height:73px
while (offset < height)
{
//format ESC* Epson printer
String modIMG = new String(new byte[]{0x1B, 0x2A, 33, 0, 2});
img.append(modIMG);
for (int x = 0; x < width; ++x) {
for (int k = 0; k < 3; ++k) {
byte slice= 0;
for (int b = 0; b < 8; ++b) {
int y = (((offset / 8) + k) * 8) + b;
int i = (y * width) + x;
boolean v = false;
if (i < bitSet.length()) {
v = bitSet.get(i);}
slice |= (byte) ((v ? 1 : 0) << (7 - b));
}
img.slice(new String(new byte[] {slice}));
}
}
offset += 24;
img.append(new String(new String(new byte[]{0x1B,0x33,30}));
}
So I solved this problem. It is not enough to set the line spacing to 24 dots using ESC '3' 24, but you have to print the image in page mode.
To enter page mode: ESC 'L'
Then you have to set the image area using ESC 'W', here I do it in C:
h2 = h * 2 + 30;
/* Set the printing area, h * 2 because of double density */
seq[0] = 0x1b;
seq[1] = 'W';
seq[2] = 0; /* xl */
seq[3] = 0; /* xh */
seq[4] = 0; /* yl */
seq[5] = 0; /* yh */
seq[6] = 0; /* dxl */
seq[7] = 2; /* dxh */
seq[8] = h2 % 256; /* dyl */
seq[9] = h2 / 256; /* dyh */
if (write(fd, seq, 10) != 10)
goto finish;
Now send the image data and finally print with sending 0x0c, this also returns the printer to standard mode.
By using page mode, the white stripes on the image went away.
BTW, this seems to be an oddity of EPSON TM-T88 printers, I neither see these white lines on the TM-T500A nor on e.g. Aures printers. On these printers I can use standard mode to print images.
I solved it setting page mode followin Marc Balmer's example, but have to send feed for 24*2 dots (line height double density) to get it working: So after every slice of data send command ESC J n bytes {27, 74, 48}. And finally got it!
Hope it helps!
Reference (must be logged)
Try sending ESC/POS command (0x1B, '3', 24) prior to printing. This sets the line spacing to 24 dots rather than the default of 30.
Just to confirm what other people have suggested, I've successfully managed to remove the white lines between the data stripes using "ESC30".
You can see the actual code (Haskell) and the results here.
I've been working to create a GPU based conway's game of life program. If you're not familiar with it, here is the Wikipedia Page. I created one version that works by keeping an array of values where 0 represents a dead cell, and 1 a live one. The kernel then simply writes to an image buffer data array to draw an image based on the cell data and then checks each cell's neighbors to update the cell array for the next execution to render.
However, a faster method instead represents the value of a cell as a negative number if dead and a positive number if alive. The number of that cell represents the amount of neighbors it has plus one (making zero an impossible value since we cannot differentiate 0 from -0). However this means that when spawning or killing a cell we must update it's eight neighbor's values accordingly. Thus unlike the working procedure, which only has to read from the neighboring memory slots, this procedure must write to those slots. Doing so is inconsistent and the outputted array is not valid. For example cells contain numbers such as 14 which indicates 13 neighbors, an impossible value. The code is correct as I wrote the same procedure on the cpu and it works as expected. After testing, I believe that when tasks try to write to the memory at the same time there is a delay that leads to a writing error of some kind. For example, perhaps there is a delay between reading the array data and setting in which time the data is changed making another task's procedure incorrect. I've tried using semaphors and barriers, but have just learned OpenCL and parallel processing and don't quite grasp them completely yet. The kernel is as follows.
int wrap(int val, int limit){
int response = val;
if(response<0){response+=limit;}
if(response>=limit){response-=limit;}
return response;
}
__kernel void optimizedModel(
__global uint *output,
int sizeX, int sizeY,
__global uint *colorMap,
__global uint *newCellMap,
__global uint *historyBuffer
)
{
// the x and y coordinates that currently being computed
unsigned int x = get_global_id(0);
unsigned int y = get_global_id(1);
int cellValue = historyBuffer[sizeX*y+x];
int neighborCount = abs(cellValue)-1;
output[y*sizeX+x] = colorMap[cellValue > 0 ? 1 : 0];
if(cellValue > 0){// if alive
if(neighborCount < 2 || neighborCount > 3){
// kill
for(int i=-1; i<2; i++){
for(int j=-1; j<2; j++){
if(i!=0 || j!=0){
int wxc = wrap(x+i, sizeX);
int wyc = wrap(y+j, sizeY);
newCellMap[sizeX*wyc+wxc] -= newCellMap[sizeX*wyc+wxc] > 0 ? 1 : -1;
}
}
}
newCellMap[sizeX*y+x] *= -1;
// end kill
}
}else{
if(neighborCount==3){
// spawn
for(int i=-1; i<2; i++){
for(int j=-1; j<2; j++){
if(i!=0 || j!=0){
int wxc = wrap(x+i, sizeX);
int wyc = wrap(y+j, sizeY);
newCellMap[sizeX*wyc+wxc] += newCellMap[sizeX*wyc+wxc] > 0 ? 1 : -1;
}
}
}
newCellMap[sizeX*y+x] *= -1;
// end spawn
}
}
}
The array output is the image buffer data used to render the
kernel's computation.
The sizeX and sizeY constants are the width and height of the image buffer respectively.
The colorMap array contains the rgb integer values for black and white respectively which are used to change the image buffer's values properly to render colors.
The newCellMap array is the updated cell map being calculated once rendering is determined.
The historyBuffer is the old state of the cells at the beginning of the kernel call. Every time the kernel is executed, this array is updated to the newCellMap array.
Additionally the wrap function makes the space toroidal. How could I fix this code such that it works as expected. And why doesn't the global memory update with each change by a task? Isn't it supposed to be shared memory?
As sharpneli said in his answer, you are reading and writing same memory zones from different threads and that gives an undefined behaviour.
Solution:
You need to split your newCellMap in 2 arrays, one for the previous execution and one where the new value will be stored. Then, you need to change the kernel arguments from the host side in each call, so that the oldvalues of the next iteration are the newvalues of the previous iteration. Due to how you structurize your algorithm, you will also need to perform a copybuffer of oldvalues to newvalues before you run it.
__kernel void optimizedModel(
__global uint *output,
int sizeX, int sizeY,
__global uint *colorMap,
__global uint *oldCellMap,
__global uint *newCellMap,
__global uint *historyBuffer
)
{
// the x and y coordinates that currently being computed
unsigned int x = get_global_id(0);
unsigned int y = get_global_id(1);
int cellValue = historyBuffer[sizeX*y+x];
int neighborCount = abs(cellValue)-1;
output[y*sizeX+x] = colorMap[cellValue > 0 ? 1 : 0];
if(cellValue > 0){// if alive
if(neighborCount < 2 || neighborCount > 3){
// kill
for(int i=-1; i<2; i++){
for(int j=-1; j<2; j++){
if(i!=0 || j!=0){
int wxc = wrap(x+i, sizeX);
int wyc = wrap(y+j, sizeY);
newCellMap[sizeX*wyc+wxc] -= oldCellMap[sizeX*wyc+wxc] > 0 ? 1 : -1;
}
}
}
newCellMap[sizeX*y+x] *= -1;
// end kill
}
}else{
if(neighborCount==3){
// spawn
for(int i=-1; i<2; i++){
for(int j=-1; j<2; j++){
if(i!=0 || j!=0){
int wxc = wrap(x+i, sizeX);
int wyc = wrap(y+j, sizeY);
newCellMap[sizeX*wyc+wxc] += oldCellMap[sizeX*wyc+wxc] > 0 ? 1 : -1;
}
}
}
newCellMap[sizeX*y+x] *= -1;
// end spawn
}
}
}
Regarding your question about shared memory has a simple answer. OpenCL does not have shared memory across HOST-DEVICE.
When you create a memory buffer for the device, you first have to init that memory zone with clEnqueueWriteBuffer() and read it with clEnqueueWriteBuffer() to get the results. Even if you do have a pointer to the memory zone, your pointer is a pointer to the host side copy of that zone. Which is likely not to have the last version of device computed output.
PD: I created long time ago a "Live" game on OpenCL, I found that the easyer and faster way to do it is simply to create a big 2D array of bits (bit addressing). And then write a piece of code without any branches that simply analize the neibours and gets the updated value for that cell. Since bit addressing is used, the amount of memory read/write by each thread is considerably lower that addressing chars/ints/other. I achieved 33Mcells/sec in a very old OpenCL HW (nVIDIA 9100M G). Just to let you know that your if/else approach is probably not the most efficient one.
Just as a reference, I let you here my implementation of the game of life (OpenCL kernel):
//Each work-item processess one 4x2 block of cells, but needs to access to the (3x3)x(4x2) block of cells surrounding it
// . . . . . .
// . * * * * .
// . * * * * .
// . . . . . .
__kernel void life (__global unsigned char * input, __global unsigned char * output){
int x_length = get_global_size(0);
int x_id = get_global_id(0);
int y_length = get_global_size(1);
int y_id = get_global_id(1);
//int lx_length = get_local_size(0);
//int ly_length = get_local_size(1);
int x_n = (x_length+x_id-1)%x_length; //Negative X
int x_p = (x_length+x_id+1)%x_length; //Positive X
int y_n = (y_length+y_id-1)%y_length; //Negative Y
int y_p = (y_length+y_id+1)%y_length; //Positive X
//Get the data of the surrounding blocks (TODO: Make this shared across the local group)
unsigned char block[3][3];
block[0][0] = input[x_n + y_n*x_length];
block[1][0] = input[x_id + y_n*x_length];
block[2][0] = input[x_p + y_n*x_length];
block[0][1] = input[x_n + y_id*x_length];
block[1][1] = input[x_id + y_id*x_length];
block[2][1] = input[x_p + y_id*x_length];
block[0][2] = input[x_n + y_p*x_length];
block[1][2] = input[x_id + y_p*x_length];
block[2][2] = input[x_p + y_p*x_length];
//Expand the block to points (bool array)
bool point[6][4];
point[0][0] = (bool)(block[0][0] & 1);
point[1][0] = (bool)(block[1][0] & 8);
point[2][0] = (bool)(block[1][0] & 4);
point[3][0] = (bool)(block[1][0] & 2);
point[4][0] = (bool)(block[1][0] & 1);
point[5][0] = (bool)(block[2][0] & 8);
point[0][1] = (bool)(block[0][1] & 16);
point[1][1] = (bool)(block[1][1] & 128);
point[2][1] = (bool)(block[1][1] & 64);
point[3][1] = (bool)(block[1][1] & 32);
point[4][1] = (bool)(block[1][1] & 16);
point[5][1] = (bool)(block[2][1] & 128);
point[0][2] = (bool)(block[0][1] & 1);
point[1][2] = (bool)(block[1][1] & 8);
point[2][2] = (bool)(block[1][1] & 4);
point[3][2] = (bool)(block[1][1] & 2);
point[4][2] = (bool)(block[1][1] & 1);
point[5][2] = (bool)(block[2][1] & 8);
point[0][3] = (bool)(block[0][2] & 16);
point[1][3] = (bool)(block[1][2] & 128);
point[2][3] = (bool)(block[1][2] & 64);
point[3][3] = (bool)(block[1][2] & 32);
point[4][3] = (bool)(block[1][2] & 16);
point[5][3] = (bool)(block[2][2] & 128);
//Process one point of the game of life!
unsigned char out = (unsigned char)0;
for(int j=0; j<2; j++){
for(int i=0; i<4; i++){
char num = point[i][j] + point[i+1][j] + point[i+2][j] + point[i][j+1] + point[i+2][j+1] + point[i][j+2] + point[i+1][j+2] + point[i+2][j+2];
if(num == 3 || num == 2 && point[i+1][j+1] ){
out |= (128>>(i+4*j));
}
}
}
output[x_id + y_id*x_length] = out; //Assign to the output the new cells value
};
Here you don't save any intermediate states, just the cell status at the end (live/death). It does not have branches, so it is quite fast in the process.
I am trying to get the RGB value of any given pixel on an image into 12 bit binary, with each channel represented by 4 bits. For example, if a pixel has it's red channel at '255', or in binary "11111111". I wish to convert that to "1111".
I have got a code working, but it is rather slow, and I am wondering if there is a better way to do this.
Here's what i did.
I use the method getRGB(x, y) to get the RGB value of one pixel.
e.g. -4278864
I convert that into 3 separate channels e.g. r 190 g 181 b 176
I divide that by 16 to get integer equivalent of 4 bit
representation
I convert the number to a binary.
Add 0's in front of the binary if the binary generated is less
than 4 bit.
Concatenate to a 12bit binary to represent 12bit color output. e.g. 101110111011
Here are my code:
import java.awt.Component;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class LoadImageApp extends Component {
private static int[] rgbArray;
private static double bitPerColor = 4.0;
public static void main(String[] args) {
BufferedImage img = null;
String fileName = "Image.jpg";
try {
//Read in new image file
img = ImageIO.read(new File("src/"+fileName));
}
catch (IOException e){
}
if (img == null) {
System.out.println("No image loaded");
}
else {
//Get RGB Value
int val = img.getRGB(500, 500);
System.out.println("rgbValue from getRGB is: " + val);
//Convert to three separate channels
int a = (0xff000000 & val) >>> 24;
int r = (0x00ff0000 & val) >> 16;
int g = (0x0000ff00 & val) >> 8;
int b = (0x000000ff & val);
System.out.println("rgbValue in RGB is: ");
System.out.println("a " + a + " r " + r + " g " + g + " b " + b);
double power = Math.pow(2.0, bitPerColor);
//Convert each channel to binary
String r4bit = Integer.toBinaryString((int)(r/(power)));
String g4bit = Integer.toBinaryString((int)(g/(power)));
String b4bit = Integer.toBinaryString((int)(b/(power)));
//Convert concatonate 0's in front to get desired bit count
int rDifference = (int)bitPerColor - r4bit.length();
int gDifference = (int)bitPerColor - g4bit.length();
int bDifference = (int)bitPerColor - b4bit.length();
for (int i = rDifference; i > 0; i--){
r4bit="0"+r4bit;}
for (int i = gDifference; i > 0; i--){
g4bit = "0"+g4bit;}
for (int i = bDifference; i > 0; i--){
b4bit = "0"+b4bit;}
//Concatonate three channel together to form one binary
String rgbValue = r4bit + g4bit + b4bit;
System.out.println("rgbValue in binary is: " + rgbValue);
}
}
}
It all works fine as as desired. However it's just really, really ugly, and slow, 2-3 seconds just to read one pixel. I was hoping to use the code to read a section of an image at a time, but i can imaging it taking AGES.
So any help would be very much appreciated.
Thanks in advance.
Your original code (in hex) is 0x00rrggbb. You want to convert this to 0x00000rgb. This will do it:
int rgb24 = ....;
int rgb12 = (rgb24 & 0x00f00000) >> 12 +
(rgb24 & 0x0000f000) >> 8 +
(rgb24 & 0x000000f0) >> 4;
If you wanted to "round" to the nearest color instead of truncating you could do this:
int r = (rgb24 & 0x00ff0000);
int g = (rgb24 & 0x0000ff00);
int b = (rgb24 & 0x000000ff);
r += (r >= 0x00f00000) ? 0x00080000 : 0;
g += (g >= 0x0000f000) ? 0x00000800 : 0;
b += (b >= 0x000000f0) ? 0x00000008 : 0;
int rgb12 = (r & 0x00f00000) >> 12 + (g & 0x0000f000) >> 8 + (b & 0x000000f0) >> 4;
This rounds up but only if the high-order 4 bits are not already 1111 (when you would risk overflow).
There are a few things that might be slowing it down, such as...
Getting each pixel individually from the BufferedImage
Using Math.pow() and possibly Integer.toBinaryString(), multiple times in the loop
Using Strings all the way through, rather than numbers like int or short. If you want a String, maybe do a single conversion from short --> String at the end.
I would probably try to do something like this...
// Get all the pixels
int pixelCount = imageWidth*imageHeight;
int[] pixelArray = new int[pixelCount];
img.getRGB(0,0,imageWidth,imageHeight,pixelArray,0,1);
// For storing the 4-bit data
short[] convertedArray = new short[pixelCount];
// calculate the conversion factor a single time
double power = Math.pow(2.0, bitPerColor);
// Loop over the pixels
for (int p=0;p<pixelCount;p++){
int pixel = pixelArray[p];
// Convert into separate channels
int a = (0xff000000 & val) >>> 24;
int r = (0x00ff0000 & val) >> 16;
int g = (0x0000ff00 & val) >> 8;
int b = (0x000000ff & val);
// Convert to 4-bit
a /= power;
r /= power;
g /= power;
b /= power;
// Create the short for the pixel (4 channels * 4 bits = a 2-byte short)
short newPixel = (a & 0x0000000f) << 24 | (r & 0x0000000f) << 16 | (g & 0x0000000f) << 8 | (b & 0x0000000f);
convertedArray[p] = newPixel;
// If you want to have the value as a String, do it here.
String binary = Short.toBinaryString(newPixel);
}
Offhand, I noticed you used the String plus operator for appending. I know it's simple code but that takes some work when it is in a loop. The operator calls a constructor to execute its function.
How big is the loop anyway?
Instead of using the plus operator, perhaps use other classes like StringBuilder or a Java equivalent of it. Tell me which class you end up using.
Investigate by timing your code!
When you want to see your possible bottlenecks, look at LOOPS in a heap or stack first since there are potentially many code executions. This should be a coding rule somewhere...
Try it out & have fun. Pls vote for my answer. I need a badge and more points :-)
Tommy Kwee
The value I have collected in the RGB when set using SetRGB will it provide me an image of binary for where the area where the image is present is white and the area whre the image is absent is black ?