在JAVA中对多边形进行抗锯齿时出现鬼白线

Posted

技术标签:

【中文标题】在JAVA中对多边形进行抗锯齿时出现鬼白线【英文标题】:Ghost white line appears when Antialiasing the polygons in JAVA 【发布时间】:2017-06-25 15:33:03 【问题描述】:

我正在尝试编写一个程序来绘制多边形并用所需的颜色填充它们。这是一个简单的绘画应用程序,但我面临的问题是当我绘制多边形并绘制它们时,多边形之间会出现一条细白线。但是当我不对多边形进行抗锯齿处理时,白线就会消失,但多边形并不平滑。真正的问题是我需要使多边形平滑,并且还需要去除白色细线。

绘制多边形的类是:

public class GrayScaleManager

    private final VisualizerController controller;

    private final BufferedImage grayScaledImage;
    private final HashMap<ToolsModel, BufferedImage> grayScaleportionList;

    public GrayScaleManager(VisualizerController drawingCanvas) 
        this.controller = drawingCanvas;
        grayScaleportionList = new HashMap<>();

        grayScaledImage = toGray(Utility.bufferedImageDeepCopy(Util.getImg()));
    


    public void grayScaleSelectedPortion(Graphics2D g, ToolsModel selectedArea) 


        g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        g.setClip((Shape) selectedArea);
        g.drawImage(grayScaledImage, 0, 0, null);
        g.setClip(null);


    

    private BufferedImage toGray(BufferedImage image) 
        int width = image.getWidth();
        int height = image.getHeight();
        for (int i = 0; i < height; i++) 
            for (int j = 0; j < width; j++) 
                Color c = new Color(image.getRGB(j, i));
                int red = (int) (c.getRed() * 0.3);
                int green = (int) (c.getGreen() * 0.59);
                int blue = (int) (c.getBlue() * 0.11);

                int sum = red + green + blue;
                Color newColor = new Color(sum, sum, sum);
                image.setRGB(j, i, newColor.getRGB());
            
        
        return image;
    
    public VisualizerController getController() 
        return controller;
    
    public HashMap<ToolsModel, BufferedImage> getGrayScaleportionList() 
        return grayScaleportionList;
    

当我运行代码时得到的图像是

实际上,代码围绕着一个程序(3 个场景):

场景1:如果代码是这样完成的

    public void grayScaleSelectedPotion(Graphics2D g, ToolsModel selectedArea)
         g.setClip((Shape) selectedArea);
         g.drawImage(grayScaledImage, 0, 0, null);
         g.setClip(null);
    

优点: 1. 如果多个图层以相同的颜色相互重合绘制,则这些图层看起来是一个单独的图层(单层)。 2.没有鬼白线。 缺点: 1.线条边缘不光滑。

场景2:如果应用渲染,只需在上面的方法中应用下面的代码。

   public void grayScaleSelectedPotion(Graphics2D g, ToolsModel selectedArea)
              g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,                                                                                RenderingHints.VALUE_ANTIALIAS_ON);
              g.setClip((Shape) selectedArea);
              g.drawImage(grayScaledImage, 0, 0, null);
              g.setClip(null);
    

优点: 1.单层。 2. 边缘光滑。 缺点: 1.出现鬼白线。

场景 3:如果渲染但 drawImage() 被移除

     public void grayScaleSelectedPotion(Graphics2D g, ToolsModel selectedArea)
              g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,                                                                                RenderingHints.VALUE_ANTIALIAS_ON);
              g.setClip((Shape) selectedArea);
           // g.drawImage(grayScaledImage, 0, 0, null);
              g.setClip(null);
      

优点: 1. 边缘光滑。 2.没有鬼白线。

缺点: 1.即使图层颜色相同,也可以清楚地看到多个图层(这是不可接受的)。

总之,三个场景中的三个缺点都应该被清除。

从@MadProgrammer 实施解决方案后,代码如下所示:

    super.paintComponent(g);
    grayImage = grayScaleManager.getGrayImage();
    BufferedImage mask = new BufferedImage(img.getWidth(),img.getHeight(),BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = mask.createGraphics();
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.setRenderingHint(RenderingHints.KEY_RENDERING,RenderingHints.VALUE_RENDER_QUALITY);
    shadeList.forEach((shape)->
        g2d.setColor(shape.getColor());
        if (shape.getColor().getAlpha() != NULL_ALPHA) 
          //g2d.fill((Shape)shape);  
        
        g2d.fill((Shape)shape);
        if (shape.getColor().getAlpha() == SELECTION_ALPHA) 
            g2d.setStroke(new BasicStroke(1));
            g2d.setColor(Color.red.brighter().brighter().brighter());
            g2d.draw((Shape) shape);
        
    );
    // g2d.dispose();
    masked = applyMask(mask,grayImage,AlphaComposite.SRC_IN);
    g.drawImage(img,0,0,null);
    g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN, 0.0f));
    g.drawImage(masked, 0, 0, this);
    g2d.dispose();
    g.dispose();



/*Added methods for the changes applymask method*/

public static BufferedImage applyMask(BufferedImage sourceImage, BufferedImage maskImage, int method) 

    System.out.println("I am in applymask");

    BufferedImage maskedImage = null;
    if (sourceImage != null) 
        System.out.println("I am in applymask in if case");

        int width = maskImage.getWidth(null);
        int height = maskImage.getHeight(null);

        maskedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
        Graphics2D mg = maskedImage.createGraphics();

        int x = (width - sourceImage.getWidth(null)) / 2;
        int y = (height - sourceImage.getHeight(null)) / 2;

        //mg.setColor(Color.RED);
        //mg.drawImage(sourceImage, x, y, null);
        mg.drawImage(sourceImage, 0, 0, null);
        mg.setComposite(AlphaComposite.getInstance(method,0.0f));

        mg.dispose();
    

    return maskedImage;


但是现在 grayScaledImage 没有绘制并且多边形重叠,并且当添加 grayScaledImage 时我们无法向多边形添加其他颜色。

【问题讨论】:

根据记忆,setClip 不会生成“软”边缘 @MadProgrammer 请你能更具体一点。 如果您使用setClip,您将无法获得抗锯齿的好处(从记忆中),请查看Soft Clipping 以获得更多想法。您还应该使用Graphics#createGraphics#dispose,这将允许您对上下文进行修改(例如使用setClip),这些修改不会继续进行 如果没有更多代码,很难 100% 确定 @MadProgrammer 感谢这些链接,我将深入研究并尝试首先实现这些链接。 【参考方案1】:

好的,这可能看起来有点令人费解,但它应该会让您更接近目标。基本上setClip 不会产生“软边缘”,你必须作弊。

基本思想是生成要捕获的区域的透明图像,这成为“蒙版”。然后,我们使用遮罩“切出”源图像的一部分,然后最终将其应用于原始图像。

这与以下示例中演示的基本概念相同...

Tinting Image in Java improvement Can I have image alpha fade from left to right in java? Java: create shadow effect on image Add glow to a basic Java rectangle Applying colour to the transparent area of a JButton image - but not that of its container How to make Circle image Label in Java?

让我试着带你了解重要的部分......

// The master image or background
master = ImageIO.read(...);
// Generate a gray scaled version of the master image
// This is what we will be cutting out
ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
grayScaled = op.filter(master, null);

int width = master.getWidth() / 4;
int height = master.getHeight() / 4;

// This simply what I'm using as the base "selection"
// But for all intents purpose, it can be anything which can
// be painted through Graphics
Shape shape = new Rectangle2D.Double(0, 0, width, height);

// The base mask, not that it's transparent
BufferedImage mask = new BufferedImage(master.getWidth(), master.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = mask.createGraphics();
// Offset the location to apply the "selection"
g2d.translate((master.getWidth() - shape.getBounds().width) / 2, (master.getHeight() - shape.getBounds().height) / 2);
// Any color will do, it could even have an alpha applied
g2d.setColor(Color.BLUE);
// Rotate it a bit, because square is boring...
shape = new Path2D.Double(shape, AffineTransform.getRotateInstance(Math.toRadians(45.0), width / 2, height / 2));
// Fill the area
g2d.fill(shape);
// Rotate again, so you can see that we're compounding the selection
shape = new Path2D.Double(shape, AffineTransform.getRotateInstance(Math.toRadians(45.0), width / 2, height / 2));
// Fill the new selection
g2d.fill(shape);        
// Clean up 
g2d.dispose();

// Now, apply the mask to the image to get a "cut out"
masked = applyMask(mask, grayScaled, AlphaComposite.SRC_IN);

现在,这是一个简单的例子。当您想要更改选择时,您可以从头开始重新创建mask,或者只是将新对象绘制到原始mask 上,然后将其重新应用于grayScaled 以生成新的masked 图像。

然后,您需要做的就是在 master 图像上以您想要的任何 Alpha 级别绘制 masked 图像..

中间那个“灰色”的星星,就是面具;)

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test 

    public static void main(String[] args) 
        new Test();
    

    public Test() 
        EventQueue.invokeLater(new Runnable() 
            @Override
            public void run() 
                try 
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                 catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) 
                    ex.printStackTrace();
                

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            
        );
    

    public class TestPane extends JPanel 

        private BufferedImage master;
        private BufferedImage grayScaled;
        private BufferedImage masked;

        public TestPane() 
            try 
                master = ImageIO.read(...);
                ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
                grayScaled = op.filter(master, null);

                int width = master.getWidth() / 4;
                int height = master.getHeight() / 4;

                Shape shape = new Rectangle2D.Double(0, 0, width, height);
                shape = new Path2D.Double(shape, AffineTransform.getRotateInstance(Math.toRadians(45.0), width / 2, height / 2));

                BufferedImage mask = new BufferedImage(master.getWidth(), master.getHeight(), BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2d = mask.createGraphics();
                g2d.translate((master.getWidth() - shape.getBounds().width) / 2, (master.getHeight() - shape.getBounds().height) / 2);
                g2d.setColor(Color.BLUE);
                g2d.fill(shape);
                shape = new Path2D.Double(shape, AffineTransform.getRotateInstance(Math.toRadians(45.0), width / 2, height / 2));
                g2d.fill(shape);            
                g2d.dispose();

                masked = applyMask(mask, grayScaled, AlphaComposite.SRC_IN);
             catch (IOException ex) 
                ex.printStackTrace();
            
        

        @Override
        public Dimension getPreferredSize() 
            return master == null ? getPreferredSize() : new Dimension(master.getWidth(), master.getHeight());
        

        @Override
        protected void paintComponent(Graphics g) 
            super.paintComponent(g);
            if (master != null && masked != null) 
                Graphics2D g2d = (Graphics2D) g.create();
                int x = (getWidth() - master.getWidth()) / 2;
                int y = (getHeight() - master.getHeight()) / 2;

                g2d.drawImage(master, x, y, this);
                g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
                g2d.drawImage(masked, x, y, this);
                g2d.dispose();
            
        

    

    public static BufferedImage applyMask(BufferedImage sourceImage, BufferedImage maskImage, int method) 

        BufferedImage maskedImage = null;
        if (sourceImage != null) 

            int width = maskImage.getWidth(null);
            int height = maskImage.getHeight(null);

            maskedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            Graphics2D mg = maskedImage.createGraphics();

            int x = (width - sourceImage.getWidth(null)) / 2;
            int y = (height - sourceImage.getHeight(null)) / 2;

            mg.drawImage(sourceImage, x, y, null);
            mg.setComposite(AlphaComposite.getInstance(method));

            mg.drawImage(maskImage, 0, 0, null);

            mg.dispose();
        

        return maskedImage;

    


您还可以随时对其中任何一个应用渲染提示,以帮助清理它,因为它只是图像,它不应该给您(很多)问题;)

【讨论】:

非常感谢您的详细描述,我会尝试并尽快通知您。 不要害怕摆弄图像参数的顺序和 AlphaComposite 类型的 applyMask,我尝试了几次让它做我想做的事情;)你可能想要查看Compositing Graphics了解更多详情 我想我需要你的帮助。我对所有这些图形代码都很生气。你能帮我解决这个问题吗,如果你有兴趣,我可以把我的项目发给你,因为我没有太多时间来完成这个项目,实现你的代码看起来很有希望,但我无法完成。那么您是否有时间看看这个并帮助解决问题。 我们创建了三个缓冲图像,一个是 editorimage 存储实际图像,grayscaled 用于灰度图像,masked 用于存储绘制对象和 alphacomposite 图像。但是这段代码不符合我们的目标,因为我们也想为绘制的对象着色,但它不接受任何颜色或不显示我们在运行时绘制的对象的任何变化。 @DilipPoudel 如果我理解正确,那么example 可以【参考方案2】:

您可以通过 Area 类创建各个剪切形状的并集来解决您的问题,然后将其用作灰度图像剪切蒙版。

【讨论】:

【参考方案3】:

我猜你的图像显示你的代码在 2D 中构建多边形,这就是为什么这显示白线感觉 2D 一个多边形与其他多边形重叠。

【讨论】:

对不起Maulin,这可能不是真正的原因,因为我已经用谷歌搜索了很多,如果多边形的边界是直线,就像我画一个正方形或矩形一样,白线不会出现不是任何白线。所以我认为它关于三角形抗锯齿的问题

以上是关于在JAVA中对多边形进行抗锯齿时出现鬼白线的主要内容,如果未能解决你的问题,请参考以下文章

openGL线型和线宽以及线的抗锯齿

在 Löve2D 中使用四边形关闭抗锯齿

第5个示例 递归反射抗锯齿

OptiX第5个示例 递归反射抗锯齿

JAVA 用drawLine画斜线出现锯齿,如何抗锯齿

如何使用 webdriver (Java) 在 phantomjs 上禁用字体抗锯齿?