如何有效地实现java.awt.Composite?

背景:我需要能够以“禁用”外观创建图像。 通常建议的方法是将图像转换为灰度并显示灰度图像。 缺点是它只适用于图像,因此显示图形很麻烦,因为您无法立即访问处于禁用状态的图像。 现在我认为这可以使用java.awt.Composite动态完成(然后我不需要知道如何实现一个Icon来禁用它)。 似乎没有实现转换为灰度,所以我必须创建自己的……

也就是说,我一起攻击了一个实现(它呈现了我所期望的)。 但我不确定它是否真的适用于所有情况(复合/ CompositeContext的Javadocs对于这种复杂的操作看起来非常薄)。 正如你从我的实现中看到的那样,我采用了一种迂回的方式来逐像素处理,因为似乎没有简单的方法来批量读取/写入像素,而这种格式并不是由所涉及的栅格决定的。

欢迎任何指向更广泛的文档/示例/提示的指针。

这是SSCCE – 它通过DisabledComposite渲染(彩色)GradientPaint,将渐变转换为灰度。 请注意,在现实世界中,您将无法知道通过哪些调用呈现的内容。 Gradient实际上只是一个例子(抱歉,但是人们常常没有这样做,所以这次我会明确说明)。

import java.awt.BorderLayout; import java.awt.Color; import java.awt.Composite; import java.awt.CompositeContext; import java.awt.Dimension; import java.awt.GradientPaint; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.ColorModel; import java.awt.image.Raster; import java.awt.image.WritableRaster; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class CompositeSSCE implements Runnable { static class DisabledComposite implements Composite { @Override public CompositeContext createContext( final ColorModel srcColorModel, final ColorModel dstColorModel, final RenderingHints hints) { return new DisabledCompositeContext(srcColorModel, dstColorModel); } } static class DisabledCompositeContext implements CompositeContext { private final ColorModel srcCM; private final ColorModel dstCM; final static int PRECBITS = 22; final static int WEIGHT_R = (int) ((1 << PRECBITS) * 0.299); final static int WEIGHT_G = (int) ((1 << PRECBITS) * 0.578); final static int WEIGHT_B = (int) ((1 << PRECBITS) * 0.114); final static int SRCALPHA = (int) ((1 << PRECBITS) * 0.667); DisabledCompositeContext(final ColorModel srcCM, final ColorModel dstCM) { this.srcCM = srcCM; this.dstCM = dstCM; } public void compose(final Raster src, final Raster dstIn, final WritableRaster dstOut) { final int w = Math.min(src.getWidth(), dstIn.getWidth()); final int h = Math.min(src.getHeight(), dstIn.getHeight()); for (int y = 0; y < h; ++y) { for (int x = 0; x >> 24) * SRCALPHA) >> PRECBITS; int gray = ( ((rgb1 >> 16) & 0xFF) * WEIGHT_R + ((rgb1 >> 8) & 0xFF) * WEIGHT_G + ((rgb1 ) & 0xFF) * WEIGHT_B ) >> PRECBITS; int rgb2 = dstCM.getRGB(dstIn.getDataElements(x, y, null)); int a2 = rgb2 >>> 24; int r2 = (rgb2 >> 16) & 0xFF; int g2 = (rgb2 >> 8) & 0xFF; int b2 = (rgb2 ) & 0xFF; // mix the two pixels gray = gray * a1 / 255; final int ta = a2 * (255 - a1); r2 = gray + (r2 * ta / (255*255)); g2 = gray + (g2 * ta / (255*255)); b2 = gray + (b2 * ta / (255*255)); a2 = a1 + (ta / 255); rgb2 = (a2 << 24) | (r2 << 16) | (g2 << 8) | b2; Object data = dstCM.getDataElements(rgb2, null); dstOut.setDataElements(x, y, data); } } } @Override public void dispose() { // nothing for this implementation } } // from here on out its only the fluff to make this a runnable example public static void main(String[] argv) { Runnable r = new CompositeSSCE(); SwingUtilities.invokeLater(r); } // simple component to use composite to render static class DemoComponent extends JComponent { // demonstrate rendering an icon in grayscale with the composite protected void paintComponent(Graphics g) { Graphics2D g2d = (Graphics2D) g; GradientPaint p = new GradientPaint(0, 0, Color.GREEN, 127, 127, Color.BLUE, true); g2d.setComposite(new DisabledComposite()); g2d.setPaint(p); g2d.fillRect(0, 0, getWidth(), getHeight()); } } // Fluff to use the Composite in Swing public void run() { try { JFrame f = new JFrame("Test grayscale composite"); DemoComponent c = new DemoComponent(); c.setPreferredSize(new Dimension(500, 300)); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setLayout(new BorderLayout()); f.add(c, BorderLayout.CENTER); f.pack(); f.setVisible(true); } catch (Exception e) { throw new RuntimeException(e); } } } 

您始终可以创建BufferedImage ,使用bufferedImage.createGraphics()绘制需要灰度缩放到该Graphics对象的任何内容,然后使用javax.swing.GreyFilter创建图像的灰度缩放副本。

此页面显示如何使用Filter

您可能还想将您的方法与SwingX的BlendComposite进行比较,我希望它与您正在做的事情相同。 BlendComposite具有允许灰度的饱和度模式。 (它还有更多模式。)

这个页面有一个BlendComposite的演示。

关于效率,我希望它们之间没有重要的区别,因为有中间步骤,从我所看到的,用两者完整的图像数据副本。 但是如果使用我建议的第一种方法保留灰度图像,则可以防止重新计算非动态控件。

如果您执行上述操作之一,性能将是正确的,我希望这是您真正想要的。

根据您的评论,我猜您可能只想将效果应用于组件。 为此,您可以使用JLayer ,仅在Java 7中可用。从Javadoc可以看到覆盖半透明绿色的示例。 你可以用灰色替换它。 如果你想在以前版本的Java中使用JXLayer ,你可以使用JXLayer ,它是SwingX项目的一部分。

如果我不理解你的问题,我很抱歉,但这可能会对你有帮助吗? 它使用css来制作图像灰度。

这也可以是你想要的? 它使用jquery和canvas来操作图像。