【问题标题】:Graphics2D transformation result does not match manual transformationGraphics2D 转换结果与手动转换不匹配
【发布时间】:2012-07-09 07:01:51
【问题描述】:

我正在使用 Java 的 Graphics2D 在组件上绘制,并使用 AffineTransform 来操作我的绘图。 Graphics2D 为此提供了一种方法变换,它采用 AffineTransform。

有时我需要在不使用内置转换的情况下手动操作一个点。 但是,当我尝试使用与 Graphics2D.transform 相同的变换来变换一个点时,有时结果点并不相同。

下面的代码重现了这个问题(它是 Scala 代码,但我认为你可以想象 Java 代码。):

   var transformationMatrix = new AffineTransform()
   /*
    * transformationMatrix is modified throughout the program
    * ...
    */
   override def paintComponent(g: Graphics2D) = {
      super.paintComponent(g)
      /* 1. transform using graphics transform */
      g.transform(transformationMatrix)
      g.setColor(Color.RED)
      g.fill(new Rectangle(0, 0, 1, 1))
      /* 2. transform point manually */
      g.setTransform(new AffineTransform) // reset transformation to standard
      val p0 = new Point(0, 0)
      val pDest = new Point()
      transformationMatrix.transform(p0, pDest)
      g.setColor(Color.BLUE)
      g.fill(new Rectangle(pDest.x, pDest.y, 1, 1)
   }

预期行为

蓝色矩形(手动计算)覆盖红色矩形(通过变换计算)。

经验丰富的行为

我承认我的 transformationMatrix 并不是真正的整数,但这应该不是问题,不是吗?

   affineTransform = 1.1, 0.0, 520.55
                     0.0, 1.1, 182.54999999999995
                     0.0, 0.0,    1.0

这是一个错误还是我错过了一些深刻的见解?

编辑:如果您将 transformationMatrix 设置为

,您可以重现该错误
transformationMatrix = new AffineTransform(1.1, 0.0, 0.0, 1.1, 521.55, 183.54999999999995)

在paintComponent 的开头。请注意,g 的类型是 Graphics2D。

【问题讨论】:

  • 我想知道调用paint() 时是否可能有不同的转换处于活动状态。我会在方法开始时尝试 g.getTransform() 并使用结果来恢复“标准”转换。
  • 这也无济于事。 g.getTransform() 返回单位矩阵并将其保存并稍后恢复会导致相同的行为。

标签: java swing scala transform graphics2d


【解决方案1】:

嗯,你在做两件不同的事情。

在 (1) 中,您正在对一个形状(与它是 Rectangle 而不是 Rectangle2D.Double 无关)进行产生分数坐标的变换。它只是绘制了别名,因为您没有设置特定的渲染提示(RenderingHints.KEY_ANTIALIASING -> RenderingHints.VALUE_ANTIALIAS_ONRenderingHints.KEY_STROKE_CONTROL -> RenderingHints.VALUE_STROKE_PURE)。

在 (2) 中,您将 一个点 进行转换,并将其强制 转换为别名坐标(Point 而不是Point2D.Double)。然后从该点依次构造一个矩形。

显然,幕后可能会发生非常不同的事情,我根本不希望转换为整数点与在混叠图形上下文中绘制浮点形状会产生相同的结果。

(未经测试)我猜想(1)的有效等效语句是

g.fill(transformationMatrix.createTransformedShape(new Rectangle(0, 0, 1, 1)))

【讨论】:

    【解决方案2】:

    每当您使用浮点坐标时,如果您想要正确的结果,您应该使用图形对象的“2D”版本。我不是从“书”中读到的,所以我不能引用,这只是对它的体验。

    这是我的丑陋 java 代码,它产生了你所期望的结果。

    AffineTransform transformationMatrix = AffineTransform.getTranslateInstance(520.55, 182.54999999999995);
    transformationMatrix.scale(1.1, 1.1);
    ((Graphics2D) previewGraphics).transform(transformationMatrix);
    previewGraphics.setColor(Color.RED);
    ((Graphics2D) previewGraphics).fill(new Rectangle(0,0,1,1));
    ((Graphics2D) previewGraphics).setTransform(new AffineTransform());
    Point2D p0 = new Point2D.Double(0, 0);
    Point2D pDest = new Point2D.Double();
    transformationMatrix.transform(p0, pDest);
    previewGraphics.setColor(Color.BLUE);
    ((Graphics2D) previewGraphics).fill((Shape) new Rectangle2D.Double(pDest.getX(), pDest.getY(), 1, 1));
    

    【讨论】:

    • 不起作用。我已经将 Graphics2D 作为 g (这在 Scala 中自动完成),即使我使用 Point2D 和 Rectangle2D 问题仍然存在。您没有遇到问题的原因是,它只发生在平移和缩放的特殊值上。此外,问题不在于 AffineTransform=>transform 的舍入(此舍入正确),而在于 Graphics2D 内部的舍入(在某些情况下有时会截断而不是舍入)。
    • @knub 如果您想发布产生错误的实际值,我很乐意在不同的 JVM 上进行测试。顺便说一句,Graphics 对象的底层实现都是原生绑定,所以问题可能是特定于平台的?
    【解决方案3】:

    当您执行第一步 g.transform(transformationMatrix) 时,图形将其与已经存在的转换组合在一起。在第二步中,您将使用 g.setTransform(new AffineTransform) 覆盖它,从而丢失之前的转换(如果有)。你假设你回到了起点,但这可能不是真的。

    在第 1 步之前创建一个 getTransform(),在第 2 步之后创建另一个以验证它们是否相同。

    【讨论】:

    • 好点!不幸的是,这不是问题。使用 println(g.getTransform) 打印转换会打印身份并使用 setTransform 而不是 transform 会导致相同的行为。
    【解决方案4】:

    您的转换基本上只是(520.55, 182.55) 的翻译。而且因为它具有小数像素值,它实际上对舍入的选择很敏感。如果您启用了抗锯齿,您实际上会得到一个 4 像素的红色斑点,覆盖重叠的像素。否则,鉴于舍入为整数和截断为整数之间的歧义,您看到的行为(分歧)是合理的。

    【讨论】:

    • 不,它不仅是翻译,我还有一个 1.1 的比例因子。我没有打开抗锯齿。所以您认为一种实现是舍入为整数(手动转换),而另一种实现是截断为整数(Graphics2D 转换)?听起来有点合理,但我想会很奇怪。任何想法如何解决这个问题?
    • @knub - 1.1 的比例因子与舍入的目的无关,这就是为什么我说它“基本上”只是一个翻译(而不是说它 只是翻译)。 Graphics2D 默认绘制样式是像素圆角和左上角,因此如果你绘制一个N*M 矩形,你会得到N*M 像素从原点开始,然后到N-1M-1 像素在x 和 y 方向。如果您必须排列单个像素没有抗锯齿,我唯一的建议是手动完成所有数学运算(并使用Shape)。 API 未承诺您想要的行为。
    • "Graphics2D 默认绘图样式为像素圆角":据我判断,并非如此: (520.55, 182.55) 圆角(=截断)为 (520, 182)。还是我错过了什么?
    • 而因子 1.1 与舍入的目的有关。没有问题,当我将缩放因子设置为 1.0(具有相同的翻译)。
    • @knub - 那么,这真的很奇怪。它甚至不影响左上角的位置。在任何情况下,我所说的“像素四舍五入”是四舍五入并左移,至少用于填充对象。如果您使用 Rectangle2D.Double(和 Point2D.Double)手动操作会发生什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-19
    相关资源
    最近更新 更多