We are about to switch to a new forum software. Until then we have removed the registration on this forum.
Question: I have a gradient. Only colors from that gradient will ever be printed to screen. Once I print a pixel to screen I need to then retrieve the RGB value of that pixel. I then need to "rotate" the color to the next spot in the gradient, either up or down. How do I do this programmatically?
Here's some code I put together from other threads.
creates some images of gradients:
PImage img1;
PImage img2;
void setup() {
size(500, 500);
color[] gradient1 = {
color(0, 0, 0),
color(0, 0, 255),
color(0, 255, 255),
color(0, 255, 0),
color(255, 255, 0),
color(255, 0, 0)
};
color[] gradient2 = {
0x000000,
0x132f97,
0x3e8c84,
0x89da49,
0xe8de17,
0xff8823,
0xfe0009
};
img1 = createGradientImage(300, 20, gradient1);
img2 = createGradientImage(300, 20, gradient2);
}
void draw() {
background(226, 225, 215);
translate(100, 200);
image(img1, 0, 0);
image(img2, 0, 40);
}
PImage createGradientImage(int w, int h, color[] colors) {
PImage img = createImage(w, h, RGB);
int divideColors = colors.length - 1;
int stepSize = img.width / divideColors;
img.loadPixels();
for (int x=0; x<img.width; x++) {
color cS = colors[x/stepSize];
color cE = colors[min((x/stepSize)+1, divideColors)];
float amt = (float) (x % stepSize) / stepSize;
color cC = lerpColor(cS, cE, amt);
for (int y=0; y<img.height; y++) {
int index = x + y * img.width;
img.pixels[index] = cC;
}
}
img.updatePixels();
return img;
}
Demonstrates the effect where leaving your mouse on a spot in the sketch should "increase the heat value" while all pixels in the sketch simultaneously decrease, creating a fade out effect from one edge of the gradient to the other. This sketch is a poor example because there's a lot of things I'm not doing correctly, it's not supposed to rotate through the entire hue/gradient continuously.
color getHeatMapColor(int value)
{
return color((0.666 * (255 - value)), 255, value*10, 255);
}
int reduceHeatMapValueBy1(int rgb)
{
float h = map(map(hue(rgb),0,255,0,169)-1/16,0,169,0,255);
float b = brightness(rgb)-1.1/16;
return color(h, 255, b, 255);
}
int increaseHeatMapValueBy1(int rgb)
{
float h = map(map(hue(rgb),0,255,0,169)+3,0,169,0,255);
float b = brightness(rgb)+3.3;
return color(h, 255, b, 255);
}
void setup()
{
size(300, 300, P2D);
colorMode(HSB);
noStroke();
background(0);
loadPixels();
imageMode(CENTER);
}
int p = 0;
void draw()
{
PImage mypixels = get(mouseX-15,mouseY-15,30,30);
mypixels.loadPixels();
for (int i = 0; i < 30*30; ++i)
{
mypixels.pixels[i] = increaseHeatMapValueBy1(mypixels.pixels[i]);
}
image(mypixels, mouseX, mouseY);
if(++p == 10)
{
loadPixels();
for (int i = 0; i < width*height; ++i)
{
pixels[i] = reduceHeatMapValueBy1(pixels[i]);
}
updatePixels();
p = 0;
}
//if (p == width*height)
//{
// //background(0);
// //ellipse(width/2, height/2, width/4, width/4);
// p = 0;
// loadPixels();
//}
}
Answers
Any time you are conceptualizing color space in terms of rotating a color wheel, I recommend using a hue-based colorspace such as HSB. Then Red both 0 and 360, and you can continue to add indefinitely, taking modulo (%) of the color as you rotate.
See the Processing Color Tutorial and the colorMode() function.
You can then also define gradients in terms of rotation distances or interpolations rather than absolute values.
I'm not sure how relevant or not the examples you posted are to your own problem. When you say:
Do you mean that you then print the next color in the pixel to the right? Or do you mean that you then replace the pixel with the next color in the following draw frame?
Like I said it doesn't work exactly how I want, it shouldn't keep rotating through all the hues, red should be maximum. Also it doesn't rotate through the hues correctly. My math in calculating the gradient is bad. Well, I want to be able to create this effect with a selection of gradients. I was thinking I'd store gradients in an array, but the most efficient way of doing this is preferred because I'll be doing this to hundreds of thousands of pixels many times per second.
I want to try different color schemes. So to answer your question, I need to take every pixel and replace the pixel with a new color value. +1 "color" means go up in the gradient, -1 color means go down in the gradient. 0 is black, and whatever the number of steps is in the gradient, that will be max (red in this case), or whatever the gradient dictates.
Just imagine how blend modes work. Light blue + light blue = dark blue (when you add two low alpha pixels) usually. I'd like light blue + light blue to = green or something, because green is higher up in the gradient in this case. Blue + green = yellow, maybe. Yellow + yellow = red because now were going really high up in the gradient, or the "heat map".
Edit: Well, I don't need to add colors, that seems a bit more complicated. I just need to step the pixel color up or down according to a gradient.
Ah, a touch heatmap with no color rotation (you don't want it to cycle). Sounds like RGB (not HSB) and a defined set of steps is the way for you to go.
Is your goal to implement it yourself as a learning project, or do you just want high-performance pixel color shifting? If the second you might want to consider looking into whether you can use optimized libraries such as PixelFlow for gradients:
Also, for a simple approach to indexing an ordered collection of Processing colors as a Java object, see this library:
You could, for example, try managing your data in a 1D or 2D array of numbers that is aligned with the pixels[] array:
...and then update changed values to pixels[] by copying the value from the corresponding ColorScheme index.
ooo yes I like that idea.
I'm not trying to learn per se, I'm trying to create a prototype of an idea. Fast easy results is preferred over efficiency. Did you have a pixelflow feature or example in mind that I could look at to do this? I'm adding the library now and will explore the examples.
works!
Congrats and nicely done, @elanhickler -- thanks for sharing your working prototype.