# blendMode and transparency

Hi,

First, thank you to all and everyone involved with Processing. I only recently discovered Processing and I instantly found it to be super inspirational.

I hope someone can help me with some guidance in relation to blendModes and transparency

I’m trying to add gradient fill to a shape. I’m doing this by drawing the shape and then drawing colored lines over the shape.

My problem is that if the background color changes, the gradient overlay shows.
Or, in other words, my implementation only creates desired result if background color is “aligned” to the blendMode.

Here is an example. Any guidance on how to achieve transparency around the shape will be highly appreciated.

``````void setup() {
size(800, 800);
noLoop();
}

void draw() {
translate(width/2, height/2);
fill(0);
ellipse(0, 0, 200, 200);

for (float i = -100; i <= 100; i++) {
float inter = map(i, -100, 100, 0, 1);
color c = lerpColor(color(255), color(0), inter);
stroke(c);
line(-100, i, 100, i);
}
}
``````
2 Likes

Hi,

Welcome to the forum!

Your post made me think of a solution without using any blendMode or transparency but rather drawing separate lines to display the gradient.

``````/**
* Draw a circle filled with a gradient at location [x, y]
*/
void gradientFilledCircle(int x, int y, int diameter, float rotation, color from, color to) {
int radius = diameter / 2;

push();
translate(x, y);
rotate(rotation);

// Draw each line

// Compute y location of the line point
float lineY = sin(acos(ratio * 2 - 1)) * radius;

// or this (from https://www.rapidtables.com/math/trigonometry/arccos/sin-of-arccos.html)
//float lineY = sqrt(1 - pow(ratio * 2 - 1, 2)) * radius;

stroke(lerpColor(from, to, ratio));

line(lineX, lineY, lineX, -lineY);
}

pop();
}

void setup() {
size(500, 500);
}

void draw() {
background(#16a085);

translate(width/2, height/2);
gradientFilledCircle(0, 0, 200, HALF_PI, color(#d35400), color(#2980b9));

noLoop();
}
``````

It now works with any background

The rotation works but when animated, there’s glitches because of the lines and float precision but it can be solved by drawing it on a separate PGraphics buffer and rotate it.

Anyway is this what you wanted to do?

2 Likes

Thanks @josephh,

Your reply is useful and I have bookmarked it for later reference.

However, I think I could have been more clear in my original post.

I used an ellipse for simplicity while what I’m actually looking to achieve should support arbitrery shapes.

``````void setup() {
size(800, 800);
noLoop();
}

void draw() {
int s = 100;
translate(width/2, height/2);
fill(0);
push();
translate(0, -s);
fill(0);
beginShape();
vertex(-s * 0.10, s * 0.10);
quadraticVertex(0, 0, s * 0.10, s * 0.1);
vertex(s * 0.10, s * 0.10);

vertex(s * 0.8, s * 0.9);
quadraticVertex(s * 0.9, s, s * 0.8, s * 1.1);
vertex(s * 0.8, s * 1.1);

vertex(s * 0.1, s * 1.9);
quadraticVertex(0, s * 2, -s * 0.1, s * 1.9);
vertex(-s * 0.1, s * 1.9);

vertex(-s * 0.8, s * 1.1);
quadraticVertex(-s * 0.9, s, -s * 0.8, s * 0.9);
vertex(-s * 0.8, s * 0.9);
endShape(CLOSE);
pop();

for (float i = -s; i <= s; i++) {
float inter = map(i, -s, s, 0, 1);
color c = lerpColor(color(255), color(0), inter);
stroke(c);
line(-s, i, s, i);
}
}
``````

1 Like

It’s kinda tough to give you advice if you’re new to Processing. What you’re asking how to do is already beyond the introductory tool set, and paradoxically becomes easier if you use some more advanced tools. You may want to consider skipping the Processing way of stacking lines. In stead, or in addition, use the underlying renderer tools directly. The default renderer is based on Java AWT, which provides some of the gradient functionality you’re looking for.

``````import processing.awt.PGraphicsJava2D;
import java.awt.geom.Point2D;
import java.awt.Color;
import java.awt.geom.Path2D;

float s = 200;

PGraphicsJava2D graphics;

Point2D start = new Point2D.Float(0.0, 0.0);
Point2D end = new Point2D.Float(400.0, 400.0);
float[] colorStops = new float[] { 0.0, 0.5, 1.0 };
Color[] colors = new Color[] {
new Color(0xffff0000, true),
new Color(0x7f7f007f, true),
new Color(0xff0000ff, true) };
start, end, colorStops, colors);

Path2D.Float path = new Path2D.Float();

void settings() {
size(400, 400, JAVA2D);
}

void setup() {
graphics = (PGraphicsJava2D)getGraphics();

path.moveTo(-s * 0.10, s * 0.10);
path.quadTo(0.0, 0.0, s * 0.10, s * 0.1);
path.lineTo(s * 0.10, s * 0.10);

path.lineTo(s * 0.8, s * 0.9);
path.quadTo(s * 0.9, s, s * 0.8, s * 1.1);
path.lineTo(s * 0.8, s * 1.1);

path.lineTo(s * 0.1, s * 1.9);
path.quadTo(0, s * 2, -s * 0.1, s * 1.9);
path.lineTo(-s * 0.1, s * 1.9);

path.lineTo(-s * 0.8, s * 1.1);
path.quadTo(-s * 0.9, s, -s * 0.8, s * 0.9);
path.lineTo(-s * 0.8, s * 0.9);

path.closePath();
}

void draw() {
blendMode(BLEND);
background(0xff202020);
translate(width * 0.5, height * 0.5);
translate(0.0, -s);
graphics.g2.fill(path);
}
``````

There are other renderers that come with Processing - based on JavaFX and OpenGL - as well as newer ones that you can find through libraries, such as Skia. Your overall strategy will depend on what renderer you use and how complex you need the gradient to be.

There are also other posts on gradients on this forum if you use the search tool in the top right corner.

Best,
Jeremy

5 Likes

Thanks a lot @behreajj !

I will read up up on alternative renderers.

Hi again!

Thanks @behreajj for the insight on how to do this with Java AWT!

Just for fun, here’s a method that support arbitrary shapes by using a mask (of black pixels) that we then fill with the gradient using `PGraphics` :

``````/**
* Draw a rhombus shape mask of size s
*/
// It's important to fill it with black

mask.vertex(-s * 0.10, s * 0.10);
mask.vertex(s * 0.10, s * 0.10);

mask.vertex(s * 0.8, s * 0.9);
mask.vertex(s * 0.8, s * 1.1);

mask.vertex(s * 0.1, s * 1.9);
mask.vertex(-s * 0.1, s * 1.9);

mask.vertex(-s * 0.8, s * 1.1);
mask.vertex(-s * 0.8, s * 0.9);

}

/**
* Take a PGraphics with some black mask and return a new image filled
*/

// We are going to compute the bounds of the mask
// And store the mask pixels in an array
int maxY = 0;

for (int x = 0; x < mask.width; x++) {
for (int y = 0; y < mask.height; y++) {
int loc = x + y * mask.width;

// If it's a black pixel
// Add it to the array

// Update min and max y
if (y < minY) minY = y;
if (y > maxY) maxY = y;
}
}
}

// Create a new graphics object

// For every pixel, draw it with the gradient
for (PVector pixel : maskPixels) {
int loc = (int) pixel.x + (int) pixel.y * gradient.width;
gradient.pixels[loc] = lerpColor(from, to, map(pixel.y, minY, maxY, 0, 1));
}

}

int shapeSize = 150;

void setup() {
size(500, 500);

mask = createGraphics(shapeSize * 2, shapeSize * 2);

}

void draw() {
background(#16a085);

// Display the final gradient centered
imageMode(CENTER);
image(gradient, width / 2, height / 2);

noLoop();
}
``````

2 Likes

Just saw that we have a competitor here!

1 Like