[英]Drawing fully transparent “white” in Java BufferedImage
這聽起來像是一個奇怪的標題,但是請允許我,這是有原因的:
我正在嘗試在灰色背景的文本周圍產生白光。
為了生成光暈,我創建了一個比文本大的新BufferedImage,然后將白色文本繪制到圖像的畫布上,並通過ConvolveOp在圖像上運行高斯模糊,以期實現以下目的:
最初,當輝光比文字的灰色背景更暗時,我感到有些驚訝:
但是經過一番思考,我理解了這個問題:
卷積在每個顏色通道(R,G,B和A)上獨立運行以計算模糊圖像。 圖片的透明背景的顏色值為0x00000000,即完全透明的黑色 ! 因此,當卷積濾鏡在圖像上運行時,它不僅會混合alpha值,還會將黑色混入白色像素的RGB值。 這就是為什么輝光發暗的原因。
要解決此問題,我需要將圖像初始化為0x00FFFFFF,即改為完全透明的白色 ,但是如果我只是設置該顏色並用它填充一個矩形,則它根本不起作用,就像Java所說的“好吧,這是一個完全透明的矩形,你在畫畫!這不會改變圖像的。讓我為你優化它。完成。不客氣。”
如果我改為將顏色設置為0x01FFFFFF,即幾乎完全透明的白色,則它確實繪制了矩形,並且發光看起來很漂亮,除非我最終在其周圍出現了一個非常暗的白色框...
有沒有一種方法可以在任何地方將圖像初始化為0x00FFFFFF?
更新:
我找到了一種方法,但是您可能會發現它不是最優的:
我在圖像上繪制了一個不透明的白色矩形,然后在圖像上運行RescaleOp ,將所有alpha值都設置為0。這是可行的,但是就性能而言,這可能是一種糟糕的方法。
我可以以某種方式做得更好嗎?
PS:我也樂於接受完全不同的建議來創建這種發光效果
初始方法使輝光顯得更暗的主要原因是您未使用帶有預乘alpha分量的圖像。 ConvolveOp的JavaDoc包含一些有關在卷積期間如何處理alpha分量的信息。
您可以使用“幾乎完全透明的白色”解決此問題。 但是,或者,您可以簡單地使用具有預乘alpha的圖像,即類型為TYPE_INT_ARGB_PRE
。
這是一個MCVE ,它在面板上繪制一些文本,以及一些圍繞文本的脈沖發光(刪除計時器並設置固定的半徑以刪除脈沖-我在這里忍不住玩了一點……)。
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class TextGlowTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new TextGlowPanel());
f.setSize(300,200);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class TextGlowPanel extends JPanel
{
private BufferedImage image;
private int radius = 1;
TextGlowPanel()
{
Timer t = new Timer(50, new ActionListener()
{
long startMillis = -1;
@Override
public void actionPerformed(ActionEvent e)
{
if (startMillis == -1)
{
startMillis = System.currentTimeMillis();
}
long d = System.currentTimeMillis() - startMillis;
double s = d / 1000.0;
radius = (int)(1 + 15 * (Math.sin(s * 3) * 0.5 + 0.5));
repaint();
}
});
t.start();
}
@Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
gr.setColor(Color.GRAY);
int w = getWidth();
int h = getHeight();
gr.fillRect(0, 0, w, h);
if (image == null || image.getWidth() != w || image.getHeight() != h)
{
// Must be prmultiplied!
image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);
}
Graphics2D g = image.createGraphics();
Font font = g.getFont().deriveFont(70.0f).deriveFont(Font.BOLD);
g.setFont(font);
g.setComposite(AlphaComposite.Src);
g.setColor(new Color(255,255,255,0));
g.fillRect(0,0,w,h);
g.setComposite(AlphaComposite.SrcOver);
g.setColor(new Color(255,255,255,0));
g.fillRect(0,0,w,h);
g.setColor(Color.WHITE);
g.drawString("Glow!", 50, 100);
image = getGaussianBlurFilter(radius, true).filter(image, null);
image = getGaussianBlurFilter(radius, false).filter(image, null);
g.dispose();
g = image.createGraphics();
g.setFont(font);
g.setColor(Color.BLUE);
g.drawString("Glow!", 50, 100);
g.dispose();
gr.drawImage(image, 0, 0, null);
}
// From
// http://www.java2s.com/Code/Java/Advanced-Graphics/GaussianBlurDemo.htm
public static ConvolveOp getGaussianBlurFilter(
int radius, boolean horizontal)
{
if (radius < 1)
{
throw new IllegalArgumentException("Radius must be >= 1");
}
int size = radius * 2 + 1;
float[] data = new float[size];
float sigma = radius / 3.0f;
float twoSigmaSquare = 2.0f * sigma * sigma;
float sigmaRoot = (float) Math.sqrt(twoSigmaSquare * Math.PI);
float total = 0.0f;
for (int i = -radius; i <= radius; i++)
{
float distance = i * i;
int index = i + radius;
data[index] =
(float) Math.exp(-distance / twoSigmaSquare) / sigmaRoot;
total += data[index];
}
for (int i = 0; i < data.length; i++)
{
data[i] /= total;
}
Kernel kernel = null;
if (horizontal)
{
kernel = new Kernel(size, 1, data);
}
else
{
kernel = new Kernel(1, size, data);
}
return new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
}
}
我發現clearRect
應該繪制透明顏色。
g.setBackground(new Color(0x00FFFFFF, true));
g.clearRect(0, 0, img.getWidth(), img.getHeight());
您還應該能夠通過直接設置像素數據來強制BufferedImage用透明顏色填充。
public static void forceFill(BufferedImage img, int rgb) {
for(int x = 0; x < img.getWidth(); x++) {
for(int y = 0; y < img.getHeight(); y++) {
img.setRGB(x, y, rgb);
}
}
}
它沒有明確記錄,但我對其進行了測試,並且setRGB
似乎接受ARGB值。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.