Fixed: (again) Bevel and Glow filters incorrect rendering

This commit is contained in:
Jindra Petřík
2025-05-12 20:14:16 +02:00
parent 9c3dc007c1
commit 2bf516ca08
3 changed files with 68 additions and 102 deletions

View File

@@ -31,8 +31,6 @@ import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.DataBufferInt;
import java.awt.image.Kernel;
import java.io.File;
import javax.imageio.ImageIO;
/**
* Filter application.
@@ -246,7 +244,7 @@ public class Filtering {
int index = y * w + x;
result[index] = RGBA.toInt(dr, dg, db, da);
result[index] = RGBA.toInt(dr, dg, db, da);
}
}
}
@@ -315,7 +313,7 @@ public class Filtering {
int index = y * w + x;
result[index] = RGBA.toInt(dr, dg, db, da);
result[index] = RGBA.toInt(dr, dg, db, da);
}
}
}
@@ -405,11 +403,20 @@ public class Filtering {
BufferedImage retImg = new BufferedImage(width, height, src.getType());
int[] srcPixels = getRGB(src);
int[] revPixels = new int[srcPixels.length];
for (int i = 0; i < srcPixels.length; i++) {
revPixels[i] = (srcPixels[i] & 0xffffff) + ((255 - ((srcPixels[i] >> 24) & 0xff)) << 24);
}
/*
float middle = 128/255f;
float realMiddle = 0.5f;
boolean wasMiddle = false;
for (int i = 0; i < ratios.length; i++) {
if (ratios[i] == middle) {
wasMiddle = true;
}
if (!wasMiddle) {
ratios[i] = ratios[i] * realMiddle / middle;
} else {
ratios[i] = realMiddle + (ratios[i] - middle) * realMiddle / (1 - middle);
}
}*/
BufferedImage gradient = new BufferedImage(512, 1, src.getType());
Graphics2D gg = gradient.createGraphics();
@@ -421,7 +428,7 @@ public class Filtering {
BufferedImage shadowInner;
BufferedImage hilightInner;
BufferedImage hilightImInner = dropShadow(src, 0, 0, angle, distance, Color.red, true, iterations, strength, true, true);
BufferedImage shadowImInner = dropShadow(src, 0, 0, angle + 180, distance, Color.blue, true, iterations, strength, true, true);
BufferedImage h2Inner = new BufferedImage(width, height, src.getType());
@@ -468,7 +475,6 @@ public class Filtering {
sc.setComposite(AlphaComposite.SrcOver);
sc.drawImage(shadowOuter, 0, 0, null);
Graphics2D retc = retImg.createGraphics();
retc.setColor(Color.black);
retc.fillRect(0, 0, width, height);
@@ -476,36 +482,17 @@ public class Filtering {
retc.drawImage(shadowIm, 0, 0, null);
retc.drawImage(hilightIm, 0, 0, null);
int[] ret = getRGB(retImg);
blur(ret, width, height, blurX, blurY, iterations);
int[] mask = null;
if (type == INNER) {
mask = srcPixels;
}
if (type == OUTER) {
mask = revPixels;
}
int[] bevel = getRGB(retImg);
blur(bevel, width, height, blurX, blurY, iterations);
for (int i = 0; i < srcPixels.length; i++) {
if (mask != null && ((mask[i] >> 24) & 0xFF) == 0) {
ret[i] = 0;
}
int ah = (int) (((ret[i] >> 16) & 0xFF) * strength);
int as = (int) ((ret[i] & 0xFF) * strength);
int ah = (int) (((bevel[i] >> 16) & 0xFF) * strength);
int as = (int) ((bevel[i] & 0xFF) * strength);
int ra = cut(ah - as, -255, 255);
ret[i] = gradientPixels[255 + ra];
bevel[i] = gradientPixels[255 + ra];
}
setRGB(retImg, width, height, ret);
if (!knockout && compositeSource) {
Graphics2D g = retImg.createGraphics();
g.setComposite(type == OUTER ? AlphaComposite.SrcOver : AlphaComposite.DstOver);
g.drawImage(src, 0, 0, null);
}
return retImg;
return compose(width, height, src, bevel, type, knockout, compositeSource);
}
public static SerializableImage glow(SerializableImage src, int blurX, int blurY, float strength, Color color, boolean inner, boolean knockout, int iterations) {
@@ -556,43 +543,45 @@ public class Filtering {
}
}
if (knockout || inner) {
for (int i = 0; i < shadow.length; i++) {
int mask = (srcPixels[i] >> 24) & 0xff;
if (!inner) {
mask = 255 - mask;
}
return compose(width, height, src, shadow, inner ? INNER : OUTER, knockout, compositeSource);
}
if (inner && compositeSource && !knockout) {
Color shadowColor = new Color(shadow[i], true);
Color srcColor = new Color(srcPixels[i], true);
srcColor = new Color(srcColor.getRed(), srcColor.getGreen(), srcColor.getBlue(), 255);
private static BufferedImage compose(int width, int height, BufferedImage srcImage, int[] pixels, int type, boolean knockout, boolean compositeSource) {
BufferedImage resultImage = new BufferedImage(width, height, srcImage.getType());
setRGB(resultImage, width, height, pixels);
Graphics2D g = resultImage.createGraphics();
Color resultColor = over(shadowColor, srcColor);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
shadow[i] = resultColor.getRGB();
}
shadow[i] = (shadow[i] & 0xffffff) + ((mask * ((shadow[i] >> 24) & 0xff) / 255) << 24);
if (type == FULL && !knockout && compositeSource) {
g.setComposite(AlphaComposite.DstOver);
g.drawImage(srcImage, 0, 0, null);
} else if (type == INNER) {
if (knockout || !compositeSource) {
g.setComposite(AlphaComposite.DstIn);
} else {
g.setComposite(AlphaComposite.DstAtop);
}
g.drawImage(srcImage, 0, 0, null);
} else if (type == OUTER) {
if (knockout) {
g.setComposite(AlphaComposite.DstOut);
g.drawImage(srcImage, 0, 0, null);
} else if (compositeSource) {
g.setComposite(AlphaComposite.SrcOver);
g.drawImage(srcImage, 0, 0, null);
}
}
BufferedImage retCanvas = new BufferedImage(width, height, src.getType());
setRGB(retCanvas, width, height, shadow);
if (!knockout && compositeSource && !inner) {
Graphics2D g = retCanvas.createGraphics();
g.setComposite(AlphaComposite.SrcOver);
g.drawImage(src, 0, 0, null);
}
return retCanvas;
return resultImage;
}
public static SerializableImage gradientGlow(SerializableImage src, int blurX, int blurY, float angle, double distance, Color[] colors, float[] ratios, int type, int iterations, float strength, boolean knockout) {
return new SerializableImage(gradientGlow(src.getBufferedImage(), blurX, blurY, angle, distance, colors, ratios, type, iterations, strength, knockout));
public static SerializableImage gradientGlow(SerializableImage src, int blurX, int blurY, float angle, double distance, Color[] colors, float[] ratios, int type, int iterations, float strength, boolean knockout, boolean compositeSource) {
return new SerializableImage(gradientGlow(src.getBufferedImage(), blurX, blurY, angle, distance, colors, ratios, type, iterations, strength, knockout, compositeSource));
}
private static BufferedImage gradientGlow(BufferedImage src, int blurX, int blurY, float angle, double distance, Color[] colors, float[] ratios, int type, int iterations, float strength, boolean knockout) {
private static BufferedImage gradientGlow(BufferedImage src, int blurX, int blurY, float angle, double distance, Color[] colors, float[] ratios, int type, int iterations, float strength, boolean knockout, boolean compositeSource) {
int width = src.getWidth();
int height = src.getHeight();
@@ -609,10 +598,6 @@ public class Filtering {
double moveX = (distance * Math.cos(angleRad));
double moveY = (distance * Math.sin(angleRad));
int[] srcPixels = getRGB(src);
int[] revPixels = new int[srcPixels.length];
for (int i = 0; i < srcPixels.length; i++) {
revPixels[i] = (srcPixels[i] & 0xffffff) + ((255 - ((srcPixels[i] >> 24) & 0xff)) << 24);
}
int[] shadow = new int[srcPixels.length];
for (int i = 0; i < srcPixels.length; i++) {
@@ -624,39 +609,13 @@ public class Filtering {
blur(shadow, width, height, blurX, blurY, iterations);
int[] mask = null;
if (type == INNER) {
mask = srcPixels;
}
if (type == OUTER) {
mask = revPixels;
}
if (mask != null) {
for (int i = 0; i < mask.length; i++) {
int m = (mask[i] >> 24);
if (m == 0) {
shadow[i] = 0;
}
}
}
for (int i = 0; i < shadow.length; i++) {
int a = (shadow[i] >> 24) & 0xff;
shadow[i] = gradientPixels[a];
int gp = gradientPixels[a];
shadow[i] = gp;
}
BufferedImage retCanvas = new BufferedImage(width, height, src.getType());
setRGB(retCanvas, width, height, shadow);
if (!knockout) {
Graphics2D retImg = retCanvas.createGraphics();
retImg.setComposite(AlphaComposite.DstOver);
retImg.drawImage(src, 0, 0, null);
}
return retCanvas;
return compose(width, height, src, shadow, type, knockout, compositeSource);
}
private static int[] getRGB(BufferedImage image) {

View File

@@ -119,13 +119,17 @@ public class GRADIENTBEVELFILTER extends FILTER {
public SerializableImage apply(SerializableImage src, double zoom, int srcX, int srcY, int srcW, int srcH) {
List<Color> colors = new ArrayList<>();
List<Float> ratios = new ArrayList<>();
float lastRatioF = 0f;
for (int i = 0; i < gradientColors.length; i++) {
float ratioF = gradientRatio[i] / 255f;
if ((i > 0) && (gradientRatio[i - 1] == gradientRatio[i])) {
continue;
ratioF = lastRatioF + 0.000001f;
}
colors.add(gradientColors[i].toColor());
ratios.add(gradientRatio[i] / 255f);
ratios.add(ratioF);
lastRatioF = ratioF;
}
float[] ratiosArr = new float[ratios.size()];
for (int i = 0; i < ratios.size(); i++) {
ratiosArr[i] = ratios.get(i);

View File

@@ -121,12 +121,15 @@ public class GRADIENTGLOWFILTER extends FILTER {
public SerializableImage apply(SerializableImage src, double zoom, int srcX, int srcY, int srcW, int srcH) {
List<Color> colors = new ArrayList<>();
List<Float> ratios = new ArrayList<>();
float lastRatioF = 0f;
for (int i = 0; i < gradientColors.length; i++) {
float ratioF = gradientRatio[i] / 255f;
if ((i > 0) && (gradientRatio[i - 1] == gradientRatio[i])) {
continue;
ratioF = lastRatioF + 0.000001f;
}
colors.add(gradientColors[i].toColor());
ratios.add(gradientRatio[i] / 255f);
ratios.add(ratioF);
lastRatioF = ratioF;
}
int type = Filtering.INNER;
if (onTop && !innerShadow) {
@@ -139,7 +142,7 @@ public class GRADIENTGLOWFILTER extends FILTER {
for (int i = 0; i < ratios.size(); i++) {
ratiosAr[i] = ratios.get(i);
}
return Filtering.gradientGlow(src, (int) Math.round(blurX * zoom), (int) Math.round(blurY * zoom), (int) (angle * 180 / Math.PI), distance * zoom, colors.toArray(new Color[colors.size()]), ratiosAr, type, passes, strength, knockout);
return Filtering.gradientGlow(src, (int) Math.round(blurX * zoom), (int) Math.round(blurY * zoom), (int) (angle * 180 / Math.PI), distance * zoom, colors.toArray(new Color[colors.size()]), ratiosAr, type, passes, strength, knockout, compositeSource);
}
@Override