如何有效地实现java.awt.Composite? [英] How can I implement java.awt.Composite efficiently?

查看:123
本文介绍了如何有效地实现java.awt.Composite?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Background: I need to be able to create imagery in "disabled" look. The commonly suggested approach is to convert images to grayscale and show the grayscaled image. The drawback is that it only works with images, making it cumbersome to show graphics where you do not have immediate access to an image in disabled state. Now I thought this could be done on the fly with java.awt.Composite (and then I would not need to know how for example an Icon is implemented to render it disabled). Only there seems to be no implementation to convert to grayscale, so I had to create my own...

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

That said, I hacked together an implementation (and it renders what I expect it to). But I'm not sure it will really work correctly for all cases (Javadocs of Composite/CompositeContext seem extremely thin for such a complex operation). And as you may see from my implementation I go a roundabout way to process pixel by pixel, as there seems to be no simple way to read/write pixels in bulk in a format not dictated by the involved rasters.

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

Any pointers to more extensive documentation / examples / hints are welcome.

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

Here's the SSCCE - it renders a (colored) GradientPaint through the DisabledComposite to convert the gradient to grayscale. Note that in the real world you will not know what is rendered an with what calls. The Gradient is really only an example (sorry, but too often people don't get that, so I'll make it explicit this time).

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 < w; ++x) {
                    int rgb1 = srcCM.getRGB(src.getDataElements(x, y, null));
                    int a1 = ((rgb1 >>> 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 ,绘制任何需要灰度的东西到 Graphics 使用 bufferedImage.createGraphics()对该图像进行对象,然后使用 javax.swing.GreyFilter 创建图像的灰度副本。

You can always create a BufferedImage, draw whatever needs to be grey-scaled to the Graphics object for that image using bufferedImage.createGraphics(), and then use the javax.swing.GreyFilter to create a grey-scaled copy of the image.

此页面显示如何使用过滤器

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

You might also want to compare your approach with SwingX's BlendComposite, which I expect does the same thing as you are doing. BlendComposite has a Saturation mode that would allow grey-scale. (It also has more modes.)

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

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

About efficiency, I expect there's no important difference between them, because there's intermediate steps, and from what I can see, complete copies of the image data with both. But if you retain the grey-scaled image using the first method I suggested, you can prevent recalculation of non-dynamic controls.

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

If you do one of the above, the performance will be correct, and I expect that's what you really want.

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

From your comments I guess you might just want to apply the effect to a component. For this you could use JLayer, only available in Java 7. From the Javadoc there is an example of overlaying a translucent green. You could replace this with grey. If you want to use JLayer in previous versions of Java you could use JXLayer, part of the SwingX project.

这篇关于如何有效地实现java.awt.Composite?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆