首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Java Graphics.fillPolygon:如何渲染右边缘和下边缘?

Java Graphics.fillPolygon:如何渲染右边缘和下边缘?
EN

Stack Overflow用户
提问于 2011-10-09 10:42:09
回答 3查看 11.3K关注 0票数 5

绘制多边形时,Java2D将不显示右侧和底边。我理解为什么要这样做。然而,我想画一些包含这些边的东西。我想到的一件事是用同样的坐标跟随fillPolygondrawPolygon,但这似乎留下了一个空白。(请参见底部的小三角形图像。)有两种可能性,但我不知道是哪种。为了启用抗锯齿,我这样做:

代码语言:javascript
复制
renderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                  RenderingHints.VALUE_ANTIALIAS_ON);
renderHints.put(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(renderHints);

一种可能是未在alpha通道上执行抗锯齿,因此间隙是由透支引起的。在这种情况下,如果alpha通道是要抗锯齿的通道,则边缘将正确邻接。另一种可能性是,这里只有一个差距。

我该如何解决这个问题呢?

另外,我不确定,但看起来多边形轮廓实际上可能太大了。也就是说,它可能比我想要包含的右边缘和下边缘走得更远。

谢谢。

-更新--

基于Hovercraft Full of Eels的一个非常好的建议,我做了一个可编译的例子:

代码语言:javascript
复制
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;

import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;

public class polygon {
   private static final int WIDTH = 20;

   public static void main(String[] args) {
      BufferedImage img = new BufferedImage(WIDTH, WIDTH, BufferedImage.TYPE_INT_ARGB);
      Graphics2D g2 = img.createGraphics();
      int[] xPoints = {WIDTH / 3, (2*WIDTH) / 3, WIDTH / 3};
      int[] yPoints = {0, WIDTH / 2, WIDTH};
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setColor(Color.green);
      g2.drawLine(0, WIDTH-1, WIDTH, WIDTH-1);
      g2.drawLine(0, 0, WIDTH, 0);
      g2.drawLine(WIDTH/3, 0, WIDTH/3, WIDTH);
      g2.drawLine((2*WIDTH/3), 0, (2*WIDTH/3), WIDTH);
      g2.setColor(Color.black);
      g2.drawPolygon(xPoints, yPoints, xPoints.length);
      g2.setColor(Color.black);
      g2.fillPolygon(xPoints, yPoints, xPoints.length);
      g2.dispose();

      ImageIcon icon = new ImageIcon(img);
      JLabel label = new JLabel(icon);

      JOptionPane.showMessageDialog(null, label);
   }
}

如果将填充的多边形保留为红色,则会得到下面的图像(放大500%),显示多边形不会一直延伸到右侧边缘。也就是说,垂直的绿色线条对应于x=(2*WIDTH)/2,尽管红色多边形包含该坐标,但它不绘制任何像素。

为了查看gap问题,我将程序中的red更改为black。在此图像中,您可以在右下角看到一个细微的间隙,其中由drawPolygon绘制的轮廓与使用fillPolygon绘制的轮廓不完全相交。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2011-10-09 10:56:10

在一个简单的可编译的可运行程序中向我们展示你的绘图代码。例如,当我试图模仿你的图像并使用RenderingHints时,它似乎产生了一个具有完整右/下边缘的合适大小的图像:

代码语言:javascript
复制
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class Foo002 {
   private static final int WIDTH = 20;

   public static void main(String[] args) {
      BufferedImage img = new BufferedImage(WIDTH, WIDTH,
            BufferedImage.TYPE_INT_ARGB);
      Graphics2D g2 = img.createGraphics();
      int[] xPoints = { WIDTH / 3, (2 * WIDTH) / 3, WIDTH / 3 };
      int[] yPoints = { 0, WIDTH / 2, WIDTH };
      g2.setColor(Color.black);
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setRenderingHint(RenderingHints.KEY_RENDERING,
            RenderingHints.VALUE_RENDER_QUALITY);
      g2.fillPolygon(xPoints, yPoints, xPoints.length);
      g2.dispose();

      ImageIcon icon = new ImageIcon(img);
      JLabel label = new JLabel(icon);
      label.setBorder(BorderFactory.createLineBorder(Color.black));
      JPanel panel = new JPanel();
      panel.add(label);

      JOptionPane.showMessageDialog(null, panel);
   }
}

如果您能向我们展示一个类似的程序来再现您的问题,那么我们可以为您提供更好的帮助。

票数 3
EN

Stack Overflow用户

发布于 2011-10-09 13:44:10

我喜欢@HFOE所显示的ImageIcon的便利性,但是这个变体可能会让它更容易看到正在发生的事情。在Graphics应用程序接口中,

绘制图形轮廓的

操作通过使用像素大小的笔在像素之间遍历无限细的路径来操作,该笔悬挂在路径上锚点的右侧。填充图形的操作是通过填充无限细路径的内部来操作的。

相比之下,Graphics2D必须遵循更复杂的抗锯齿规则,这允许它“在线外绘制”。

代码语言:javascript
复制
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
import javax.swing.JPanel;

/** @see http://stackoverflow.com/questions/7701097 */
public class PixelView extends JPanel {

    private static final int SIZE = 20;
    private static final int SCALE = 16;
    private BufferedImage img;

    public PixelView(Color fill) {
        this.setBackground(Color.white);
        this.setPreferredSize(new Dimension(SCALE * SIZE, SCALE * SIZE));
        img = new BufferedImage(SIZE, SIZE, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2 = img.createGraphics();
        int[] xPoints = {SIZE / 3, (2 * SIZE) / 3, SIZE / 3};
        int[] yPoints = {0, SIZE / 2, SIZE};
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setColor(Color.green);
        g2.drawLine(0, SIZE - 1, SIZE, SIZE - 1);
        g2.drawLine(0, 0, SIZE, 0);
        g2.drawLine(SIZE / 3, 0, SIZE / 3, SIZE);
        g2.drawLine((2 * SIZE / 3), 0, (2 * SIZE / 3), SIZE);
        g2.setColor(Color.black);
        g2.drawPolygon(xPoints, yPoints, xPoints.length);
        g2.setColor(fill);
        g2.fillPolygon(xPoints, yPoints, xPoints.length);
        g2.dispose();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(img, 0, 0, getWidth(), getHeight(), null);
    }

    private static void display() {
        JFrame f = new JFrame("PixelView");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLayout(new GridLayout(1, 0));
        f.add(new PixelView(Color.black));
        f.add(new PixelView(Color.red));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                display();
            }
        });
    }
}
票数 2
EN

Stack Overflow用户

发布于 2020-08-29 16:42:00

有时是"the graphics pen hangs down and to the right from the path it traverses",有时不是。我不清楚如何预测它什么时候会发生,但我观察到,RenderingHints.VALUE_STROKE_PURE有时可以通过反复试验来改变行为。特别是,我发现如果您在程序中的drawPolygon()调用期间打开STROKE_PURE,它将使它们与您的fillPolygon()调用相匹配。

我做了一个小小的研究,展示了STROKE_CONTROL提示的效果,这是以下之一:

  • STROKE_NORMALIZE (默认设置,在我的system)
  • STROKE_PURE

在以下呼叫中:

  • drawLine()
  • drawPolygon()
  • fillPolygon()

在两种抗锯齿模式下:

  • ANTIALIASING_OFF
  • ANTIALIASING_ON

而且还有一个更令人恼火的维度显然也很重要:

在screen

  • rendered到BufferedImage.

上直接呈现到JComponent的

以下是直接渲染到可见JComponent时的结果:

下面是渲染为BufferedImage时的结果:

(请注意两种图片不同的两种情况,即直接渲染与BufferedImage渲染不同: ANTIALIAS_OFF/STROKE_NORMALIZE/fillPolygon和ANTIALIAS_OFF/STROKE_PURE/drawPolygon。)

总体而言,整件事似乎没有太多的押韵或理由。但是我们可以根据上面的图片做以下具体的观察:

观察#1:如果您希望抗锯齿drawPolygon()和抗锯齿fillPolygon()很好地匹配(最初的问题),那么在抗锯齿drawPolygon()调用期间打开STROKE_PURE。(在抗锯齿的fillPolygon()调用期间,它是否打开并不重要。)

观察#2:如果你想要抗锯齿的fillPolygon()和非抗锯齿的fillPolygon()匹配(因为,比方说,你的应用程序允许用户打开和关闭抗锯齿,而你不希望每次他们这样做时图片都会跳转到西北和东南方向),那么在非抗锯齿的fillPolygon()调用期间打开STROKE_PURE。

这是我用来生成上面图片的程序。我的结果是在linux上用opensdk11编译和运行的;我想知道是否有人在不同的平台上得到了任何不同的结果。

代码语言:javascript
复制
/** Study the effect of STROKE_PURE. */

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;

@SuppressWarnings("serial")
public final class AntiAliasingStudy {

  // These can be fiddled with.
  final static int patchWidth = 24;  // keep this a multiple of 4 for sanity
  final static int patchHeight = 20;  // keep this a multiple of 4 for sanity
  final static int borderThickness = 4;
  final static int mag = 6;

  // derived quantities
  final static int totalWidth = 5*borderThickness + 4*patchWidth;
  final static int totalHeight = 4*borderThickness + 3*patchHeight;

  private static void drawLittleStudy(Graphics2D g2d,
                                      int x00, int y00,
                                      int patchWidth, int patchHeight, int borderThickness, int totalWidth, int totalHeight) {
    g2d.setColor(new java.awt.Color(240,240,240));
    g2d.fillRect(x00,y00,totalWidth, totalHeight);

    for (int row = 0; row < 3; ++row) {
      for (int col = 0; col < 4; ++col) {
        int x0 = x00 + borderThickness + col*(patchWidth+borderThickness);
        int y0 = y00 + borderThickness + row*(patchHeight+borderThickness);
        int x1 = x0 + patchWidth;
        int y1 = y0 + patchHeight;
        g2d.setColor(java.awt.Color.WHITE);
        g2d.fillRect(x0, y0, patchWidth, patchHeight);

        boolean antialias = (col >= 2);
        boolean pure = (col % 2 == 1);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialias ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, pure ? RenderingHints.VALUE_STROKE_PURE : RenderingHints.VALUE_STROKE_NORMALIZE);

        g2d.setColor(java.awt.Color.RED);
        if (row == 0) {
          // lines (drawLine)
          // diagonals
          g2d.drawLine(x0,y1, x1,y0);
          g2d.drawLine(x0,y0, x1,y1);
          // orthogonals
          g2d.drawLine((x0+patchWidth/4),y0, (x0+patchWidth*3/4),y0);
          g2d.drawLine((x0+patchWidth/4),y1, (x0+patchWidth*3/4),y1);
          g2d.drawLine(x0,(y0+patchHeight/4), x0,(y0+patchHeight*3/4));
          g2d.drawLine(x1,(y0+patchHeight/4), x1,(y0+patchHeight*3/4));
        } else if (row == 1) {
          // outlines (drawPolygon)
          // A stopsign
          g2d.drawPolygon(new int[] {x0+patchWidth/2-2, x0, x0, x0+patchWidth/2-2, x0+patchWidth/2+2, x1, x1, x0+patchWidth/2+2},
                          new int[] {y0, y0+patchHeight/2-2, y0+patchHeight/2+2, y1, y1, y0+patchHeight/2+2, y0+patchHeight/2-2, y0},
                          8);
        } else if (row == 2) {
          // fill (fillPolygon)
          // A stopsign
          g2d.fillPolygon(new int[] {x0+patchWidth/2-2, x0, x0, x0+patchWidth/2-2, x0+patchWidth/2+2, x1, x1, x0+patchWidth/2+2},
                          new int[] {y0, y0+patchHeight/2-2, y0+patchHeight/2+2, y1, y1, y0+patchHeight/2+2, y0+patchHeight/2-2, y0},
                          8);
        }
      }
    }
  }  // drawLittleStudy

  // Show a study, previously created by drawLittleStudy(), magnified and annotated.
  private static void showMagnifiedAndAnnotatedStudy(Graphics g,
                                              BufferedImage studyImage,
                                              int x00, int y00,
                                              int patchWidth, int patchHeight, int borderThickness, int totalWidth, int totalHeight, int mag,
                                              ImageObserver imageObserver) {
    // Magnify the image
    g.drawImage(studyImage,
                /*dst*/ x00,y00,x00+totalWidth*mag,y00+totalHeight*mag,
                /*src*/ 0,0,totalWidth,totalHeight,
                imageObserver);

    // Draw annotations on each picture in black,
    // in the highest quality non-biased mode
    // (now that we know what that is!)
    g.setColor(java.awt.Color.BLACK);
    ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
    for (int row = 0; row < 3; ++row) {
      for (int col = 0; col < 4; ++col) {
        int x0 = borderThickness + col*(patchWidth+borderThickness);
        int y0 = borderThickness + row*(patchHeight+borderThickness);
        int x1 = x0 + patchWidth;
        int y1 = y0 + patchHeight;
        if (false) {
          g.drawLine(x00+x0*mag,y00+y0*mag, x00+x1*mag,y00+y0*mag);
          g.drawLine(x00+x1*mag,y00+y0*mag, x00+x1*mag,y00+y1*mag);
          g.drawLine(x00+x1*mag,y00+y1*mag, x00+x0*mag,y00+y1*mag);
          g.drawLine(x00+x0*mag,y00+y1*mag, x00+x0*mag,y00+y0*mag);
        }
        if (row == 0) {
          // diagonals
          g.drawLine(x00+x0*mag,y00+y1*mag, x00+x1*mag,y00+y0*mag);
          g.drawLine(x00+x0*mag,y00+y0*mag, x00+x1*mag,y00+y1*mag);
          // orthogonals
          g.drawLine(x00+(x0+patchWidth/4)*mag,y00+y0*mag, x00+(x0+patchWidth*3/4)*mag,y00+y0*mag);
          g.drawLine(x00+(x0+patchWidth/4)*mag,y00+y1*mag, x00+(x0+patchWidth*3/4)*mag,y00+y1*mag);
          g.drawLine(x00+x0*mag,y00+(y0+patchHeight/4)*mag, x00+x0*mag,y00+(y0+patchHeight*3/4)*mag);
          g.drawLine(x00+x1*mag,y00+(y0+patchHeight/4)*mag, x00+x1*mag,y00+(y0+patchHeight*3/4)*mag);
        } else {  // row 1 or 2
          // A stopsign
          g.drawPolygon(new int[] {x00+(x0+patchWidth/2-2)*mag, x00+x0*mag, x00+x0*mag, x00+(x0+patchWidth/2-2)*mag, x00+(x0+patchWidth/2+2)*mag, x00+x1*mag, x00+x1*mag, x00+(x0+patchWidth/2+2)*mag},
                        new int[] {y00+y0*mag, y00+(y0+patchHeight/2-2)*mag, y00+(y0+patchHeight/2+2)*mag, y00+y1*mag, y00+y1*mag, y00+(y0+patchHeight/2+2)*mag, y00+(y0+patchHeight/2-2)*mag, y00+y0*mag},
                        8);
        }
      }
    }
    FontMetrics fm = g.getFontMetrics();
    {
      String[][] texts = {
          {"ANTIALIAS_OFF", "STROKE_NORMALIZE"},
          {"ANTIALIAS_OFF", "STROKE_PURE"},
          {"ANTIALIAS_ON", "STROKE_NORMALIZE"},
          {"ANTIALIAS_ON", "STROKE_PURE"},
      };
      for (int col = 0; col < 4; ++col) {
        int xCenter = borderThickness*mag + col*(patchWidth+borderThickness)*mag + patchWidth*mag/2;
        {
          int x = x00 + xCenter - fm.stringWidth(texts[col][0])/2;
          int y = y00 + 3*(patchHeight+borderThickness)*mag + fm.getAscent();
          g.drawString(texts[col][0], x,y);
          x = xCenter - fm.stringWidth(texts[col][1])/2;
          y += fm.getHeight();
          g.drawString(texts[col][1], x,y);
        }
      }
    }
    {
      String[] texts = {
        "drawLine",
        "drawPolygon",
        "fillPolygon",
      };
      for (int row = 0; row < 3; ++row) {
        int yCenter = y00 + borderThickness*mag + row*(patchHeight+borderThickness)*mag + patchHeight*mag/2;
        int x = x00 + 4*(patchWidth+borderThickness)*mag + 10;
        g.drawString(texts[row], x,yCenter);
      }
    }
  }  // showMagnifiedAndAnnotatedStudy


  private static Dimension figureOutPreferredSize(FontMetrics fm) {
    int preferredWidth = (totalWidth-borderThickness)*mag + 10 + fm.stringWidth("drawPolygon") + 9;
    int preferredHeight = fm.getHeight() + totalHeight + (totalHeight-borderThickness)*mag + 2*fm.getHeight() + 2;
    return new Dimension(preferredWidth, preferredHeight);
  }

  private static class IndirectExaminationView extends JComponent {
    public IndirectExaminationView() {
      setFont(new Font("Times", Font.PLAIN, 12));
      setPreferredSize(figureOutPreferredSize(getFontMetrics(getFont())));
    }
    @Override public void paintComponent(Graphics g) {
      FontMetrics fm = g.getFontMetrics();
      g.setColor(java.awt.Color.BLACK);
      g.drawString("through BufferedImage:", 0,fm.getAscent());
      // The following seem equivalent
      java.awt.image.BufferedImage studyImage = new java.awt.image.BufferedImage(totalWidth, totalHeight, java.awt.image.BufferedImage.TYPE_INT_ARGB);
      //java.awt.image.BufferedImage studyImage = (BufferedImage)this.createImage(totalWidth, totalHeight);
      drawLittleStudy(studyImage.createGraphics(),
                      0,0,
                      patchWidth, patchHeight, borderThickness, totalWidth, totalHeight);
                    Graphics2D studyImageGraphics2D = studyImage.createGraphics();
      g.drawImage(studyImage,
                  /*dst*/ 0,fm.getHeight(),totalWidth,fm.getHeight()+totalHeight,
                  /*src*/ 0,0,totalWidth,totalHeight,
                  this);
      showMagnifiedAndAnnotatedStudy(g, studyImage,
                                     0,fm.getHeight()+totalHeight,
                                     patchWidth, patchHeight, borderThickness, totalWidth, totalHeight, mag, this);
    }
  }  // DirectExaminationView

  private static class DirectExaminationView extends JComponent {
    public DirectExaminationView() {
      setFont(new Font("Times", Font.PLAIN, 12));
      setPreferredSize(figureOutPreferredSize(getFontMetrics(getFont())));
    }
    private BufferedImage imgFromTheRobot = null;
    @Override public void paintComponent(Graphics g) {
      final FontMetrics fm = g.getFontMetrics();
      g.setColor(java.awt.Color.BLACK);
      g.drawString("direct to JComponent:", 0,fm.getAscent());
      drawLittleStudy((Graphics2D)g,
                      0,fm.getHeight(),
                      patchWidth, patchHeight, borderThickness, totalWidth, totalHeight);

      if (imgFromTheRobot != null) {
        System.out.println("              drawing image from robot");
        showMagnifiedAndAnnotatedStudy(g, imgFromTheRobot,
                                       0, fm.getHeight()+totalHeight,
                                       patchWidth, patchHeight, borderThickness, totalWidth, totalHeight, mag, this);
        imgFromTheRobot = null;
      } else {
        System.out.println("              scheduling a robot");
        g.drawString("*** SCREEN CAPTURE PENDING ***", 0, fm.getHeight()+totalHeight+fm.getHeight()+fm.getHeight());
        // Most reliable way to do it seems to be to put it on a timer after a delay.
        Timer timer = new Timer(1000/2, new ActionListener() {
          @Override public void actionPerformed(ActionEvent ae) {
            System.out.println("            in timer callback");
            Robot robot;
            try {
              robot = new Robot();
            } catch (AWTException e) {
              System.err.println("caught AWTException: "+e);
              throw new Error(e);
            }

            Point myTopLeftOnScreen = getLocationOnScreen();
            Rectangle rect = new Rectangle(
              myTopLeftOnScreen.x, myTopLeftOnScreen.y + fm.getHeight(),
              totalWidth,totalHeight);
            BufferedImage img = robot.createScreenCapture(rect);
            imgFromTheRobot = img;
            repaint();
            System.out.println("            out timer callback");
          }
        });
        timer.setRepeats(false);
        timer.start();
      }
    }
  }  // DirectExaminationView

  public static void main(String[] args) {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      @Override public void run()
      {
        final JFrame directFrame = new JFrame("direct to JComponent") {{
          getContentPane().add(new DirectExaminationView());
          setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          pack();
          setLocation(0,0);
          setVisible(true);
        }};
        new JFrame("through BufferedImage") {{
          getContentPane().add(new IndirectExaminationView());
          setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          pack();
          setLocation(directFrame.getWidth(),0);
          setVisible(true);
        }};
      }
    });
  }
}  // class AntiAliasingStudy
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7701097

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档