how do i make the black pixels in a PImage transparent? - processing

The following code makes a rising smoke effect.
PImage buffer1;
PImage buffer2;
PImage cooling;
PImage buffer3;
float yInitial = 0.0;
void setup() {
size(600, 200);
buffer1 = createImage(width, 200, RGB);
buffer2 = createImage(width, 200, RGB);
buffer3 = createImage(width, 200, RGB);
cooling = createImage(width, 200, RGB);
}
void newLine(int rows) {
//create row of white pixels
buffer1.loadPixels();
for (int x = 0; x< buffer1.width; x++){
for(int j = 0; j< rows; j++){
//find location to draw pixels
int y = buffer1.height - (j+1);
int index = x + y * buffer1.width;
buffer1.pixels[index] = color(255);
}
}
buffer1.updatePixels();
}
void cool(){
cooling.loadPixels();
//start x at 0
float xoff = 0.0;
float increment = 0.02;
for (int x = 0; x < cooling.width; x++){
xoff += increment;
//start y at 0
float yoff = yInitial;
for (int y = 0; y < cooling.height; y++){
yoff += increment;
//calculate noise and enlarge
float n = noise(xoff, yoff);
if(n<0.4)
n = 0;
float bright = noise(xoff, yoff) *25;
//set pixel to grayscale value
cooling.pixels[x+y*cooling.width]= color(bright);
}
}
cooling.updatePixels();
yInitial += increment;
}
void draw(){
cool();
newLine(10);
background(0);
buffer1.loadPixels();
buffer2.loadPixels();
//look through all x and y coordinates and find the neightboring pixels colour
for (int x = 1; x < buffer1.width-1; x++) {
for (int y = 1; y < buffer1.height-1; y++) {
int index0 = x + y * buffer1.width;
int index1 = (x+1) + y * buffer1.width;
int index2 = (x-1) + y * buffer1.width;
int index3 = x + (y+1) * buffer1.width;
int index4 = x + (y-1) * buffer1.width;
color c1 = buffer1.pixels[index1];
color c2 = buffer1.pixels[index2];
color c3 = buffer1.pixels[index3];
color c4 = buffer1.pixels[index4];
color c5 = cooling.pixels[index0];
float newC = brightness(c1) + brightness(c2)+ brightness(c3) + brightness(c4);
newC = newC - brightness(c5);
newC = newC / 4;
buffer2.pixels[index4] = color(newC);
}
}
buffer2.updatePixels();
//swap
PImage temp = buffer1;
buffer1 = buffer2;
buffer2 = temp;
image(buffer2, 0, 0);
}
I intend to use this code to draw the moving smoke over an image that will be in the background (i haven't put this in yet). To do so I attempted to make all the black pixels in the PImage transparent. This should mean only the smoke will be drawn over the background image. I then changed all the transparent pixels back to black after the PImage had been drawn. However, when I added this in the smoke completely stopped being drawn. What have I done wrong and is this the correct way to try to draw the smoke over a background image? Here is my code:
PImage buffer1;
PImage buffer2;
PImage cooling;
PImage buffer3;
float yInitial = 0.0;
void setup() {
size(600, 200);
buffer1 = createImage(width, 200, RGB);
buffer2 = createImage(width, 200, RGB);
buffer3 = createImage(width, 200, RGB);
cooling = createImage(width, 200, RGB);
}
void newLine(int rows) {
//create row of white pixels
buffer1.loadPixels();
for (int x = 0; x< buffer1.width; x++){
for(int j = 0; j< rows; j++){
//find location to draw pixels
int y = buffer1.height - (j+1);
int index = x + y * buffer1.width;
buffer1.pixels[index] = color(255);
}
}
buffer1.updatePixels();
}
void cool(){
cooling.loadPixels();
//start x at 0
float xoff = 0.0;
float increment = 0.02;
for (int x = 0; x < cooling.width; x++){
xoff += increment;
//start y at 0
float yoff = yInitial;
for (int y = 0; y < cooling.height; y++){
yoff += increment;
//calculate noise and enlarge
float n = noise(xoff, yoff);
if(n<0.4)
n = 0;
float bright = noise(xoff, yoff) *25;
//set pixel to grayscale value
cooling.pixels[x+y*cooling.width]= color(bright);
}
}
cooling.updatePixels();
yInitial += increment;
}
void draw(){
cool();
newLine(10);
buffer1.loadPixels();
buffer2.loadPixels();
//look through all x and y coordinates and find the neightboring pixels colour
for (int x = 1; x < buffer1.width-1; x++) {
for (int y = 1; y < buffer1.height-1; y++) {
int index0 = x + y * buffer1.width;
int index1 = (x+1) + y * buffer1.width;
int index2 = (x-1) + y * buffer1.width;
int index3 = x + (y+1) * buffer1.width;
int index4 = x + (y-1) * buffer1.width;
color c1 = buffer1.pixels[index1];
color c2 = buffer1.pixels[index2];
color c3 = buffer1.pixels[index3];
color c4 = buffer1.pixels[index4];
color c5 = cooling.pixels[index0];
float newC = brightness(c1) + brightness(c2)+ brightness(c3) + brightness(c4);
newC = newC - brightness(c5);
newC = newC / 4;
buffer2.pixels[index4] = color(newC);
//make the black pixels transparent
if(color(buffer2.pixels[index0])==color(0));
buffer2.pixels[index0] = color(0,0);
if(color(buffer1.pixels[index0])==color(0));
buffer1.pixels[index0] = color(0,0);
}
}
buffer2.updatePixels();
//swap
PImage temp = buffer1;
buffer1 = buffer2;
buffer2 = temp;
image(buffer2, 0, 0);
//set transparent pixels back to black
for (int x = 1; x < buffer1.width-1; x++) {
for (int y = 1; y < buffer1.height-1; y++) {
int index0 = x + y * buffer1.width;
if(color(buffer2.pixels[index0])==color(0,0))
buffer2.pixels[index0] = color(0);
if(color(buffer1.pixels[index0])==color(0,0));
buffer1.pixels[index0] = color(0);
}
}
}

That's a pretty cool smoke effect.
There's an easier to composite the smoke effect using blendMode().
What you're after is something like blendMode(SCREEN); or blendMode(ADD); which would make black pixels on the effect image transparent.
PImage buffer1;
PImage buffer2;
PImage cooling;
PImage buffer3;
float yInitial = 0.0;
void setup() {
size(600, 200);
fill(192,0,0);
// change blend mode to treat black pixels as transparent
blendMode(SCREEN);
buffer1 = createImage(width, 200, RGB);
buffer2 = createImage(width, 200, RGB);
buffer3 = createImage(width, 200, RGB);
cooling = createImage(width, 200, RGB);
}
void newLine(int rows) {
//create row of white pixels
buffer1.loadPixels();
for (int x = 0; x< buffer1.width; x++){
for(int j = 0; j< rows; j++){
//find location to draw pixels
int y = buffer1.height - (j+1);
int index = x + y * buffer1.width;
buffer1.pixels[index] = color(255);
}
}
buffer1.updatePixels();
}
void cool(){
cooling.loadPixels();
//start x at 0
float xoff = 0.0;
float increment = 0.02;
for (int x = 0; x < cooling.width; x++){
xoff += increment;
//start y at 0
float yoff = yInitial;
for (int y = 0; y < cooling.height; y++){
yoff += increment;
//calculate noise and enlarge
float n = noise(xoff, yoff);
if(n<0.4)
n = 0;
float bright = noise(xoff, yoff) *25;
//set pixel to grayscale value
cooling.pixels[x+y*cooling.width]= color(bright);
}
}
cooling.updatePixels();
yInitial += increment;
}
void updateSmokeEffect(){
cool();
newLine(10);
buffer1.loadPixels();
buffer2.loadPixels();
//look through all x and y coordinates and find the neightboring pixels colour
for (int x = 1; x < buffer1.width-1; x++) {
for (int y = 1; y < buffer1.height-1; y++) {
int index0 = x + y * buffer1.width;
int index1 = (x+1) + y * buffer1.width;
int index2 = (x-1) + y * buffer1.width;
int index3 = x + (y+1) * buffer1.width;
int index4 = x + (y-1) * buffer1.width;
color c1 = buffer1.pixels[index1];
color c2 = buffer1.pixels[index2];
color c3 = buffer1.pixels[index3];
color c4 = buffer1.pixels[index4];
color c5 = cooling.pixels[index0];
float newC = brightness(c1) + brightness(c2)+ brightness(c3) + brightness(c4);
newC = newC - brightness(c5);
newC = newC / 4;
buffer2.pixels[index4] = color(newC);
}
}
buffer2.updatePixels();
swapBuffers();
}
void swapBuffers(){
//swap
PImage temp = buffer1;
buffer1 = buffer2;
buffer2 = temp;
}
void draw(){
background(0);
// test drawing something in the background
ellipse(mouseX,mouseY,30,30);
updateSmokeEffect();
// render blended image on top
image(buffer2, 0, 0);
}
You can use blendMode(BLEND) to revert back to the default blend mode:
void draw(){
background(0);
blendMode(ADD);
// test drawing something in the background
ellipse(mouseX,mouseY,30,30);
updateSmokeEffect();
// render blended image on top
image(buffer2, 0, 0);
blendMode(BLEND);
// test drawing something in the foreground
ellipse(mouseX + 35,mouseY,30,30);
}
Additionally you can checkout PGraphics to mimic layers, though for pixel effects like that you can do plenty with PImage (and it's set() / copy() / blend() / etc. methods)
You might also be interested shaders: fragment shaders in particular for "pixel" manipulation on the GPU

Related

Using p5.js to change the processing codes and display the shape

I want to change Processing code to p5.js. I tried to write the code of p5.js, but it cannot be displayed anything on p5.js. The original file and my code are shown below.
This is my code:
function setup(){
createCanvas(400,400);
}
var N = 100;
var cx = [0.000, 1.000, 0.500];
var cy = [0.000, 0.000, 0.866];
var x = 0.0, y = 0.0;
function draw(){
for (var i = 0; i < N; i++) {
nextPoint();
drawPoint();
}
}
function drawPoint(){
strokeWeight(1);
var px = map(x,0,1.0,0,300);
var py = map(y,0,1.0,0,300);
point(px, py);
}
function nextPoint() {
let r = random(3);
x = (x + cx[r]) / 2.0;
y = (y + cy[r]) / 2.0;
}
This is source code:(form processing)
void setup(){
size(400,400);
}
int N = 100;
float[] cx = { 0.000, 1.000, 0.500 };
float[] cy = { 0.000, 0.000, 0.866 };
float x = 0.0, y = 0.0;
void draw(){
for (int i = 0; i < N; i++) {
nextPoint();
drawPoint();
}
}
void drawPoint(){
strokeWeight(1);
float px = map(x,0,1.0,0,300);
float py = map(y,0,1.0,0,300);
point(px, py);
}
void nextPoint() {
int r = (int)random(3);
x = (x + cx[r]) / 2.0;
y = (y + cy[r]) / 2.0;
}
You need to use floor() when you define r. Although JavaScript doesn't have rigid data types for its variables like Java, it still doesn't know what you mean when you say something like cx[1.348]. When you use non-integer values for array access, you get cx[1.348] = NaN, and then when you try to draw a point at NaN, NaN, it doesn't do anything. So your code should say let r = floor(random(3)) on line 23 (you might also consider setting the background color in setup()).

How can you write an algorithm to properly fill a circle using lines from the center?

Currently I try to write code for calculating the parts of the screen you can see and those who can't because of objects that block light in 2d, like in Among Us:
The code should run on a processor with very low specs (at least in 2020), the C64. On such a simple CPU it's not possible to do such complex math fast enough for a game, so I came up with an idea: First of all, I make everything tile based, that makes processing easier and also means that I can just change entire characters or their color cells. Then I just write code for the PC in Processing (that's a coding language similar to Java but easier to use) to calculate how rays of light would move (the following graphic should make that more understandable), first just with a rectangle (and a single quadrant):
Then I wrote some completely messy assembler code for using the recorded coordinates to just keep filling the tiles with an inverted character based on the number of the ray currently being drawn on the ray until they hit an object (/ the tile it wants to fill is not inverted and not a space) and then just go to the next ray. I reduced the radius to 7 so it just takes up 256 bytes, useful for ASM. And that totally worked, I was able to fix every single bug and the result was quite impressive, since I needed to add pause statements or everything ran so fast that you couldn't see anything.
After that worked, I tried it with a circle, setting the points using this code:
int pointNum = ceil(radius * PI * 2); // calculates the circumference
for(int i = 0;i < pointNum;i++){
float angle = map(i, 0, pointNum, 0, PI*2);
setPixel(sin(angle) * radius, cos(angle) * radius);
}
I previously used the Bresenham circle algorithm but that didn't quite work so I tried a more simple way. So ...
All the marked black tiles never get hit by any light, which is a pretty big issue, because it wouldn't make much sense in a game that you just can't see those tiles. The code I used, written in Processing, is:
float[] xPoints = new float[0];
float[] yPoints = new float[0];
float[] xPointsT;
float[] yPointsT;
float[] xPointsHad = new float[0];
float[] yPointsHad = new float[0];
int pos = 0;
float interpolPos = 0;
int radius = 12;
float tileSize = 800.0 / (2*radius+1);
String output = " !byte ";
int pointNum = ceil(radius * PI * 2);
void setup() {
size(800, 800);
frameRate(60);
xPointsT = new float[0];
yPointsT = new float[0];
/*for(int i = 0;i <= radius;i++){
setPixel(radius, i);
setPixel(i, radius);
}*/ //Uncomment this and comment the next 4 lines to get the rectangle version
for(int i = 0;i < pointNum;i++){
float angle = map(i, 0, pointNum, 0, PI*2);
setPixel(sin(angle) * radius, cos(angle) * radius);
}
xPoints = concat(xPoints, xPointsT);
yPoints = concat(yPoints, yPointsT);
}
void draw(){
if(interpolPos > radius){
pos++;
interpolPos = 0;
println(output);
output = " !byte ";
}
float x=0, y=0;
float interpolMul = interpolPos / radius;
x = xPoints[pos] * interpolMul;
y = yPoints[pos] * interpolMul;
interpolPos+=1;//sorta the resolution
background(0);
stroke(255);
for(int i = 0;i < 2*radius+1;i++){
for(int j = 0;j < 2*radius+1;j++){
if((round(x) + radius) == i && (round(y) + radius) == j){
fill(0, 255, 0);
if(output != " !byte ")
output += ", ";
output += i-radius;
output += ", ";
output += j-radius;
xPointsHad = append(xPointsHad, i);
yPointsHad = append(yPointsHad, j);
}
else{
int fillVal = 0;
for(int k = 0; k < xPoints.length;k++){
if(round(xPoints[k])+radius == i && round(yPoints[k])+radius == j){
fillVal += 64;
}
}
fill(0, 0, fillVal);
if(fillVal == 0){
for(int k = 0; k < xPointsHad.length;k++){
if(round(xPointsHad[k]) == i && round(yPointsHad[k]) == j){
fill(128, 0, 0);
}
}
}
}
rect(i * tileSize, j * tileSize, tileSize, tileSize);
}
}
strokeWeight(3);
stroke(0, 255, 255, 64);
for(int i = 0;i < xPoints.length;i++){
line((float(radius)+0.5) * tileSize, (float(radius)+0.5) * tileSize, (float(radius)+0.5+xPoints[i]) * tileSize, (float(radius)+0.5+yPoints[i]) * tileSize);
}
strokeWeight(1);
fill(255, 255, 0);
ellipse((x + radius + 0.5) * tileSize, (y + radius + 0.5) * tileSize, 10, 10);
}
void setPixel(float _x, float _y){
for(int i = 0; i < xPoints.length;i++){
if(_x == xPoints[i] && _y == yPoints[i]){
return;
}
}
for(int i = 0; i < xPointsT.length;i++){
if(_x == xPointsT[i] && _y == yPointsT[i]){
return;
}
}
xPointsT = append(xPointsT, _x);
yPointsT = append(yPointsT, _y);
}
(Instructions to get the rectangle are in the code)
Those mentioned tiles seem to be never hit because the rays on them just jump over them, but what can I do to prevent that? You can decrease interpolPos+=x; to hit more tiles because that way your steps are smaller, but that wastes quite some space, so I don't think that's a good solution. Ideally you could also just decrease the number of coordinates you draw to get a smaller vision. Has anyone a good idea how to do that?
You have chosen wrong method to find all touched cells - instead of point-based way you need cell(squares)-based approach - ray intersects rectangle rather than point.
There is article of Amanatides and Woo "A Fast Voxel Traversal Algorithm for Ray Tracing" for 2D.
Practical implementation.
Example:
Quick-made tracing example. Rays emitted from left top corner go to blue points. If ray meets black cell obstacle, it stops. Pink cells are lighted by rays, grey ones are not.
Okay, I found something that worked for me in my situation: I just used the part that totally works (the rectangle) and then just make that a circle by ignoring every tile hit that's further away from the light source then the radius + 0.5, because without + .5 the circle looks weird. You can try it yourself, here's the code:
float[] xPoints = new float[0];
float[] yPoints = new float[0];
float[] xPointsT;
float[] yPointsT;
float[] xPointsHad = new float[0];
float[] yPointsHad = new float[0];
int pos = 0;
float interpolPos = 0;
int radius = 7;
float tileSize = 800.0 / (2*radius+1);
int pointNum = ceil(radius * PI * 2);
String standardOutput = " !align 15,0\n !byte ";
void setup() {
size(800, 800);
frameRate(60);
xPointsT = new float[0];
yPointsT = new float[0];
for(int i = 0;i <= radius;i++){
setPixel(radius, i);
setPixel(i, radius);
} //Uncomment this and comment the next 4 lines to get the rectangle version
/*for(int i = 0;i < pointNum;i++){
float angle = map(i, 0, pointNum, 0, PI*2);
setPixel(sin(angle) * radius, cos(angle) * radius);
}*/
xPoints = concat(xPoints, xPointsT);
yPoints = concat(yPoints, yPointsT);
xPointsT = new float[0];
yPointsT = new float[0];
}
void draw(){
if(interpolPos > radius){
pos++;
interpolPos = 0;
String output = standardOutput;
for(int i = 0;i < radius + 1;i++){
int indexPos = floor(map(i, 0, radius + 1, 0, xPointsT.length));
output += round(xPointsT[indexPos]);
output += ",";
output += round(yPointsT[indexPos]);
if(i < radius){
output += ", ";
}
}
println(output);
xPointsT = new float[0];
yPointsT = new float[0];
}
float x=0, y=0;
float interpolMul = interpolPos / radius;
x = xPoints[pos] * interpolMul;
y = yPoints[pos] * interpolMul;
interpolPos+=1;//sorta the resolution
background(0);
stroke(255);
for(int i = 0;i < 2*radius+1;i++){
for(int j = 0;j < 2*radius+1;j++){
if((round(x) + radius) == i && (round(y) + radius) == j && sqrt(sq(round(x)) + sq(round(y))) < radius + 0.5){
fill(0, 255, 0);
xPointsT = append(xPointsT, i-radius);
yPointsT = append(yPointsT, j-radius);
xPointsHad = append(xPointsHad, i);
yPointsHad = append(yPointsHad, j);
}
else{
int fillVal = 0;
for(int k = 0; k < xPoints.length;k++){
if(round(xPoints[k])+radius == i && round(yPoints[k])+radius == j){
fillVal += 64;
}
}
fill(0, 0, fillVal);
if(fillVal == 0){
for(int k = 0; k < xPointsHad.length;k++){
if(round(xPointsHad[k]) == i && round(yPointsHad[k]) == j){
fill(128, 0, 0);
}
}
}
}
rect(i * tileSize, j * tileSize, tileSize, tileSize);
}
}
strokeWeight(3);
stroke(0, 255, 255, 64);
for(int i = 0;i < xPoints.length;i++){
line((float(radius)+0.5) * tileSize, (float(radius)+0.5) * tileSize, (float(radius)+0.5+xPoints[i]) * tileSize, (float(radius)+0.5+yPoints[i]) * tileSize);
}
strokeWeight(1);
fill(255, 255, 0);
ellipse((x + radius + 0.5) * tileSize, (y + radius + 0.5) * tileSize, 10, 10);
}
void setPixel(float _x, float _y){
for(int i = 0; i < xPoints.length;i++){
if(_x == xPoints[i] && _y == yPoints[i]){
return;
}
}
for(int i = 0; i < xPointsT.length;i++){
if(_x == xPointsT[i] && _y == yPointsT[i]){
return;
}
}
xPointsT = append(xPointsT, _x);
yPointsT = append(yPointsT, _y);
}
Besides the main difference to ignore tiles that are not in the circle, I also changed that I store the coordinates not in a String but in two arrays, because then I use code to stretch them when there are fewer then radius + 1 points, so I don't have to store multiple circles with different sizes in the C64's RAM, so it meets my main requirements: It should fill every tile and it should be downscalable by ignoring some points at the end of rays. And is if efficient? Uh ... there could be a better solution that fills the circle with fewer rays, but I don't care too much. Still, if you have an idea, it would be nice if you could tell me, but otherwise this question is solved.
Edit: I forgot to add a picture. Don't be confused, I modified the code after posting it so you can also see the blue tiles on the circle.

How do I make a variable randomly equal 1 of 2 numbers? (Processing)

I'm trying for the first time to make Pong. I don't always want the ball to go to the bottom right by adding 3 every single time. How would I make it so it will either do 3, or -3, but no number in between? I know that "||" doesn't work for integers, and "random(-3,3) has the chance of giving me numbers like "0.1" which wouldn't really function in here.
Code:
float circleX = 640/2;
float circleY = 360/2;
float xSpeed = 3;
float ySpeed = 3;
float Color = (255);
float circleHeight = 32;
float circleWidth = 32;
float xAcceleration = -1.0;
float yAcceleration = -1.0;
float paddleColor = 255;
float MyPaddleX = 630;
float OpPaddleX = 10;
float MyPaddleWidth = 10;
float OpPaddleWidth = -10;
void setup() {
size(640, 360);
frameRate(60);
}
void draw() {
background(0);
//Ball
fill(Color);
ellipse(circleX, circleY, circleWidth, circleHeight);
xSpeed = //(WHAT TO PUT HERE?)
circleX = circleX + xSpeed;
circleY = circleY + ySpeed;
//My Paddle
fill(paddleColor);
rect(MyPaddleX,mouseY,MyPaddleWidth,100);
//Bouncing
if (circleX >= OpPaddleX && OpPaddleX + OpPaddleWidth >= circleX) {
xSpeed = xSpeed * xAcceleration;
}
// Top/Bottom Bouncing
if (circleY > height || circleY < 0) {
ySpeed = ySpeed * yAcceleration;
}
//My Paddle Bounceback
if (circleY >= mouseY && circleY <= mouseY + 100) {
if (circleX >= MyPaddleX && circleX <= MyPaddleX + 3)
xSpeed = xSpeed * xAcceleration;
}
//Opponent Paddle
fill(paddleColor);
rect(OpPaddleX,circleY - 50,OpPaddleWidth,100);
//if (circleX < OpPaddleX || circleX > MyPaddleX) {
// circleX = width/2;
// circleY = height/2;
// xSpeed = 0;
// ySpeed = 0;
//}
}
You can generate a number between 0 and 1 and then compare that generated number to 0.5 to "flip a coin" in your code.
Think about it this way: when you call random(1), you'll get a value between 0 and 1. Half of those values will be less than 0.5, the other half will be greater than (or equal to) 0.5.
So you can do something like this:
float x;
if(random(1) < .5){
x = -3;
}
else{
x = 3;
}
You could expand this to choose from more numbers using else if statements, or you could shorten it into a single line of code using the ternary operator:
float x = random(1) < .5 ? 3 : -3;

Processing mirror image over x axis?

I was able to copy the image to the location but not able to mirror it. what am i missing?
PImage img;
float srcY;
float srcX;
int destX;
int destY;
img = loadImage("http://oldpalmgolfclub.com/wp-content/uploads/2012/02/Palm- Beach-State-College2-e1329949470871.jpg");
size(img.width, img.height * 2);
image(img, 0, 0);
image(img, 0, 330);
int num_pixels = img.width * img.height;
int copiedWidth = 319 - 254;
int copiedHeight = 85 - 22;
int startX = (width / 2) - (copiedWidth / 2);
int startY = (height / 2) - (copiedHeight / 2);
How about simply scaling by -1 on the x axis ?
PImage img;
img = loadImage("https://processing.org/img/processing-web.png");
size(img.width, img.height * 2);
image(img,0,0);
scale(-1,1);//flip on X axis
image(img,-img.width,img.height);//draw offset
This can be achieved by manipulating pixels as well, but needs a bit of arithmetic:
PImage img;
img = loadImage("https://processing.org/img/processing-web.png");
size(img.width, img.height * 2);
int t = millis();
PImage flipped = createImage(img.width,img.height,RGB);//create a new image with the same dimensions
for(int i = 0 ; i < flipped.pixels.length; i++){ //loop through each pixel
int srcX = i % flipped.width; //calculate source(original) x position
int dstX = flipped.width-srcX-1; //calculate destination(flipped) x position = (maximum-x-1)
int y = i / flipped.width; //calculate y coordinate
flipped.pixels[y*flipped.width+dstX] = img.pixels[i];//write the destination(x flipped) pixel based on the current pixel
}
//y*width+x is to convert from x,y to pixel array index
flipped.updatePixels()
println("done in " + (millis()-t) + "ms");
image(img,0,0);
image(flipped,0,img.height);
The above can be achieved using get() and set(), but using the pixels[] array is faster. A single for loop is generally faster than using 2 nested for loops to traverse the image with x,y counters:
PImage img;
img = loadImage("https://processing.org/img/processing-web.png");
size(img.width, img.height * 2);
int t = millis();
PImage flipped = createImage(img.width,img.height,RGB);//create a new image with the same dimensions
for(int y = 0; y < img.height; y++){
for(int x = 0; x < img.width; x++){
flipped.set(img.width-x-1,y,img.get(x,y));
}
}
println("done in " + (millis()-t) + "ms");
image(img,0,0);
image(flipped,0,img.height);
You can copy a 1px 'slice'/column in a single for loop and which is faster(but still not as fast as direct pixel manipulation):
PImage img;
img = loadImage("https://processing.org/img/processing-web.png");
size(img.width, img.height * 2);
int t = millis();
PImage flipped = createImage(img.width,img.height,RGB);//create a new image with the same dimensions
for(int x = 0 ; x < flipped.width; x++){ //loop through each columns
flipped.set(flipped.width-x-1,0,img.get(x,0,1,img.height)); //copy a column in reverse x order
}
println("done in " + (millis()-t) + "ms");
image(img,0,0);
image(flipped,0,img.height);
There are other alternatives like accessing the java BufferedImage (although this means the Processing sketch will work in Java Mode mostly) or using a PShader, but these approaches are more complex. It's generally a good idea to keep things simple (especially when getting started).

Greyscale Image from YUV420p data

From what I have read on the internet the Y value is the luminance value and can be used to create a grey scale image. The following link: https://web.archive.org/web/20141230145627/http://bobpowell.net/grayscale.aspx, has some C# code on working out the luminance of a bitmap image :
{
Bitmap bm = new Bitmap(source.Width,source.Height);
for(int y=0;y<bm.Height;y++) public Bitmap ConvertToGrayscale(Bitmap source)
{
for(int x=0;x<bm.Width;x++)
{
Color c=source.GetPixel(x,y);
int luma = (int)(c.R*0.3 + c.G*0.59+ c.B*0.11);
bm.SetPixel(x,y,Color.FromArgb(luma,luma,luma));
}
}
return bm;
}
I have a method that returns the YUV values and have the Y data in a byte array. I have the current piece of code and it is failing on Marshal.Copy – attempted to read or write protected memory.
public Bitmap ConvertToGrayscale2(byte[] yuvData, int width, int height)
{
Bitmap bmp;
IntPtr blue = IntPtr.Zero;
int inputOffSet = 0;
long[] pixels = new long[width * height];
try
{
for (int y = 0; y < height; y++)
{
int outputOffSet = y * width;
for (int x = 0; x < width; x++)
{
int grey = yuvData[inputOffSet + x] & 0xff;
unchecked
{
pixels[outputOffSet + x] = UINT_Constant | (grey * INT_Constant);
}
}
inputOffSet += width;
}
blue = Marshal.AllocCoTaskMem(pixels.Length);
Marshal.Copy(pixels, 0, blue, pixels.Length); // fails here : Attempted to read or write protected memory
bmp = new Bitmap(width, height, width, PixelFormat.Format24bppRgb, blue);
}
catch (Exception)
{
throw;
}
finally
{
if (blue != IntPtr.Zero)
{
Marshal.FreeHGlobal(blue);
blue = IntPtr.Zero;
}
}
return bmp;
}
Any help would be appreciated?
I think you have allocated pixels.Length bytes, but are copying pixels.Length longs, which is 8 times as much memory (a long is 64 bits or 8 bytes in size).
You could try:
blue = Marshal.AllocCoTaskMem(Marshal.SizeOf(pixels[0]) * pixels.Length);
You might also need to use int[] for pixels and PixelFormat.Format32bppRgb in the Bitmap constructor (as they are both 32 bits). Using long[] gives you 64 bits per pixel which isn't what a 24 bit pixel format is expecting.
You might end up with shades of blue instead of grey though - depends on what your values of UINT_Constant and INT_Constant are.
There is no need to do "& 0xff", as yuvData[] already contains a byte.
Here are another couple of approaches you could try.
public Bitmap ConvertToGrayScale(byte[] yData, int width, int height)
{
// 3 * width bytes per scanline, rounded up to a multiple of 4 bytes
int stride = 4 * (int)Math.Ceiling(3 * width / 4.0);
byte[] pixels = new byte[stride * height];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
byte grey = yData[y * width + x];
pixels[y * stride + 3 * x] = grey;
pixels[y * stride + 3 * x + 1] = grey;
pixels[y * stride + 3 * x + 2] = grey;
}
}
IntPtr pixelsPtr = Marshal.AllocCoTaskMem(pixels.Length);
try
{
Marshal.Copy(pixels, 0, pixelsPtr, pixels.Length);
Bitmap bitmap = new Bitmap(
width,
height,
stride,
PixelFormat.Format24bppRgb,
pixelsPtr);
return bitmap;
}
finally
{
Marshal.FreeHGlobal(pixelsPtr);
}
}
public Bitmap ConvertToGrayScale(byte[] yData, int width, int height)
{
// 3 * width bytes per scanline, rounded up to a multiple of 4 bytes
int stride = 4 * (int)Math.Ceiling(3 * width / 4.0);
IntPtr pixelsPtr = Marshal.AllocCoTaskMem(stride * height);
try
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
byte grey = yData[y * width + x];
Marshal.WriteByte(pixelsPtr, y * stride + 3 * x, grey);
Marshal.WriteByte(pixelsPtr, y * stride + 3 * x + 1, grey);
Marshal.WriteByte(pixelsPtr, y * stride + 3 * x + 2, grey);
}
}
Bitmap bitmap = new Bitmap(
width,
height,
stride,
PixelFormat.Format24bppRgb,
pixelsPtr);
return bitmap;
}
finally
{
Marshal.FreeHGlobal(pixelsPtr);
}
}
I get a black image with a few pixel in the top left corner if I use this code and this is stable when running :
public static Bitmap ToGrayscale(byte[] yData, int width, int height)
{
Bitmap bm = new Bitmap(width, height, PixelFormat.Format32bppRgb);
Rectangle dimension = new Rectangle(0, 0, bm.Width, bm.Height);
BitmapData picData = bm.LockBits(dimension, ImageLockMode.ReadWrite, bm.PixelFormat);
IntPtr pixelStateAddress = picData.Scan0;
int stride = 4 * (int)Math.Ceiling(3 * width / 4.0);
byte[] pixels = new byte[stride * height];
try
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
byte grey = yData[y * width + x];
pixels[y * stride + 3 * x] = grey;
pixels[y * stride + 3 * x + 1] = grey;
pixels[y * stride + 3 * x + 2] = grey;
}
}
Marshal.Copy(pixels, 0, pixelStateAddress, pixels.Length);
bm.UnlockBits(picData);
}
catch (Exception)
{
throw;
}
return bm;
}

Resources