Skip to content

Instantly share code, notes, and snippets.

@volfegan
Last active April 25, 2022 03:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save volfegan/62d6f58cb8d057044360e7ab4e3b132e to your computer and use it in GitHub Desktop.
Save volfegan/62d6f58cb8d057044360e7ab4e3b132e to your computer and use it in GitHub Desktop.
A burn/dissolving image effect that flows using either a 2D noise map or some image pixel brightness
//Reference:
//https://www.youtube.com/watch?v=cLkORfzQPEU
//https://github.com/leonardo-ono/Java2DIntegrationDisintegrationEffectTest/blob/main/src/Test.java
float flowValue=350;//354~255 (display full image), -54~Zero (no image)
float flowDirection=-1;//1 (re-creating) or -1 (burning/dissolving)
float[][] flowMap; //2D map of brightness on how to proceed the dissolving effect
PImage source;
String filename;
//there is no file validation, so any non-source selected file will crash the program
void fileSelected(File selection) {
if (selection == null) {
println("No image file selected.");
exit();
} else {
String filepath = selection.getAbsolutePath();
filename = selection.getName();
int pos = filename.lastIndexOf(".");
if (pos != -1) filename = filename.substring(0, pos); //remove extension
println("File selected " + filepath);
// load file here
source = loadImage(filepath);
}
}
void interrupt() {
while (source==null) delay(200);
}
void settings() {
selectInput("Select an image file to process:", "fileSelected");
interrupt(); //interrupt process until source is selected
//for testing
//source = loadImage("cat.jpg");
width = source.width;
height = source.height;
//the canvas window size will be according to the source size
//if the source is bigger, it will be resized to 80% of display
if (width > displayWidth) {
float resizer = width / (displayWidth * 0.8);
width = (int)((float)displayWidth * 0.8);
height = (int)((float)height / resizer);
source.resize(width, height);
}
if (height > displayHeight) {
float resizer = height / (displayHeight * 0.8);
height = (int)((float)displayHeight * 0.8);
width = (int)((float)width / resizer);
source.resize(width, height);
}
size(width, height);
}
void setup() {
boolean saveImage = true;
flowMap = buildFlowMapFrom2DNoise(source, saveImage);
//flowMap = buildFlowMapFromImage(source, saveImage);
//image(source, 0, 0);
}
void draw() {
clear();
//dissolve/burn/regrow image (we are actually redrawing the image where values are below the parameter)
loadPixels();
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int level = (int)flowMap[x][y] & 255;//ensure only numbers cap at 255
if (abs(level - flowValue) < 8) {
color white = color(255);
pixels[x+y*width] = color(white);
} else if (abs(level - flowValue) < 16) {
if (flowDirection < 0) {
color yellow = color(255, 255, 0);
pixels[x+y*width] = color(yellow);
} else {
color cyan = color(0, 255, 255);
pixels[x+y*width] = color(cyan);
}
} else if (abs(level - flowValue) < 24) {
if (flowDirection < 0) {
color red = color(255, 0, 0);
pixels[x+y*width] = color(red);
} else {
//color blue = color(0, 0, 255);
//pixels[x+y*width] = color(blue);
color gray = color(255/2);
pixels[x+y*width] = color(gray);
}
} else if (level < flowValue) {
pixels[x+y*width] = source.get(x, y);
}
}
}
updatePixels();
flowValue += 1*flowDirection;
//change effect direction when above 255 + time or below 0 + time
if (flowValue > 355 || flowValue < -55) {
flowDirection*=-1;
flowMap = buildFlowMapFrom2DNoise(source, false);
//flowMap = buildFlowMapFromImage(source, false);
}
}
/*
* Creates a 2D noise map of brightness the same size as the image (also creates a image flowMap2Dnoise.jpg)
* @param PImage source
* @param boolean saveImage
* @return float[][] flowMapFrom2DNoise, whose values are 0~255
*/
float[][] buildFlowMapFrom2DNoise(PImage source, boolean saveImage) {
PImage flowMap2Dnoise = createImage(source.width, source.height, RGB);
float[][] flowMapFrom2DNoise = new float[source.width][source.height];
float detail = random(0.4, 0.6);
float noiseStep = random(0.01, 0.03);
noiseDetail(8, detail);
if (saveImage) flowMap2Dnoise.loadPixels();
// For every x,y coordinate, calculate a noise value as a brightness value
float xoff = 0;//noise x offset coordinate
for (int x = 0; x < source.width; x++) {
xoff += noiseStep;
float yoff = 0;//noise y offset coordinate
for (int y = 0; y < source.height; y++) {
yoff += noiseStep;
// Calculate the noise and scale by 255
float bright = noise(xoff, yoff) * 255;
// Set each pixel as a grayscale value
if (saveImage) flowMap2Dnoise.pixels[x+y*source.width] = color(bright);
flowMapFrom2DNoise[x][y] = color(bright);
}
}
if (saveImage) {
flowMap2Dnoise.updatePixels();
image(flowMap2Dnoise, 0, 0);
save("flowMap2Dnoise.jpg");
clear();
}
return flowMapFrom2DNoise;
}
/*
* Creates a brightness map from the image (also creates a image flowMap2Dnoise.jpg)
* @param PImage source
* @param boolean saveImage
* @return float[][] flowMapFrom2DNoise, whose values are 0~255
*/
float[][] buildFlowMapFromImage(PImage source, boolean saveImage) {
PImage flowMapFromImage = createImage(source.width, source.height, RGB);
float[][] flowMapFrom2DNoise = new float[source.width][source.height];
if (saveImage) flowMapFromImage.loadPixels();
// For every x,y coordinate, calculate a noise value as a brightness value
for (int x = 0; x < source.width; x++) {
for (int y = 0; y < source.height; y++) {
color pixel = source.pixels[x+y*source.width];
float bright = brightness(pixel);
// Set each pixel as a grayscale value
if (saveImage) flowMapFromImage.pixels[x+y*source.width] = color(bright);
flowMapFrom2DNoise[x][y] = color(bright);
}
}
if (saveImage) {
flowMapFromImage.updatePixels();
image(flowMapFromImage, 0, 0);
save("flowMapFromImage.jpg");
clear();
}
return flowMapFrom2DNoise;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment