5

ポリゴンを描画する場合、Java2Dは右端と下端を省略します。なぜこれが行われるのか理解しています。しかし、それらのエッジを含むものを描きたいと思います。私が思いついたのは、同じ座標でフォローすることでしたfillPolygondrawPolygon、これはギャップを残しているようです。(下部の小さな三角形の画像を参照してください。)2つの可能性がありますが、どちらかはわかりません。アンチエイリアスを有効にするために、私はこれを行っています:

renderHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
                  RenderingHints.VALUE_ANTIALIAS_ON);
renderHints.put(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(renderHints);

1つの可能性は、アンチエイリアシングがアルファチャネルで実行されていないため、ギャップがオーバードローによって引き起こされていることです。その場合、アルファチャネルがアンチエイリアス処理されていたものであれば、エッジは適切に隣接します。もう1つの可能性は、ここにギャップがあることです。

どうすればこれを修正できますか?

また、よくわかりませんが、ポリゴンの輪郭が大きすぎるようです。つまり、含めたい右端と下端よりも遠くにある可能性があります。

ありがとう。

ここに画像の説明を入力してください

- アップデート -

Hovercraft Full of Eelsによる非常に素晴らしい提案に基づいて、私はコンパイル可能な例を作成しました。

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、赤のポリゴンにはその座標が含まれていますが、そこにはピクセルがペイントされていません。

ここに画像の説明を入力してください

ギャップの問題を確認するためredに、プログラムをに変更しましたblack。この画像では、右下に微妙なギャップがあり、によって描かれた輪郭がで描かれたdrawPolygonものと完全に一致していませんfillPolygon

ここに画像の説明を入力してください

4

3 に答える 3

3

簡単なコンパイル可能な実行可能なプログラムで、描画用のコードを表示してください。たとえば、私があなたの画像を模倣してRenderingHintsを使用しようとすると、完全な右/下のエッジを持つ適切なサイズの画像が生成されるように見えました。

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);
   }
}

あなたがあなたの問題を再現する同様のプログラムを私たちに見せてくれるなら、私たちはあなたにもっと良い助けを与えることができます。

于 2011-10-09T02:56:09.873 に答える
2

@HFOEで示されているの便利さが気に入っていますImageIconが、このバリエーションにより、何が起こっているのかが少しわかりやすくなる場合があります。GraphicsAPIから、

図形の輪郭を描く操作は、パスのアンカーポイントの右側にぶら下がっているピクセルサイズのペンを使用して、ピクセル間の無限に細いパスをトラバースすることによって動作します。フィギュアを埋める操作は、その無限に細いパスの内部を埋めることによって動作します。

対照的に、Graphics2Dアンチエイリアシングのより複雑なルールに従う必要があります。これにより、「線の外側に描画」できます。

PixelView

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();
            }
        });
    }
}
于 2011-10-09T05:44:09.553 に答える
1

「グラフィックスペンが、通過するパスから右にぶら下がっている」場合と、そうでない場合があります。いつ予測するか、いつ予測しないかを明確に予測する方法はわかりませんが、RenderingHints.VALUE_STROKE_PUREを使用して、試行錯誤によって動作を変更できる場合があることを確認しました。特に、プログラムでdrawPolygon()呼び出し中にSTROKE_PUREをオンにすると、必要に応じてfillPolygon()呼び出しと一致することがわかりました。

次のいずれかであるSTROKE_CONTROLヒントの効果を示す少しの調査を行いました。

  • STROKE_NORMALIZE(私のシステムのデフォルト)
  • STROKE_PURE

次の呼び出しで:

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

両方のアンチエイリアスモードで:

  • ANTIALIASING_OFF
  • ANTIALIASING_ON

そして、明らかに重要なもう1つの厄介な側面があります。

  • 画面上のJComponentに直接レンダリングされます
  • BufferedImageにレンダリングされます。

表示されているJComponentに直接レンダリングした場合の結果は次のとおりです。 ここに画像の説明を入力してください

そして、BufferedImageにレンダリングしたときの結果は次のとおりです。 ここに画像の説明を入力してください

(2つの画像が異なる2つの場合、つまり直接レンダリングが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を使用してコンパイルして実行した結果です。誰かが異なるプラットフォームで異なる結果を得るかどうか知りたいです。

/** 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
于 2020-08-29T08:41:59.633 に答える