如何使用BlendMode.Difference解决ScalaFX / JavaFX中的绘制XOR伪像问题?

时间:2013-06-30 19:05:16

标签: scala 2d javafx rubber-band scalafx

我正在将一些代码从Java移植到Scala,并且在尝试“橡皮筋”时遇到绘制人工制品的问题 - 即绘制一个随鼠标指针移动的矩形。

这在Java2D中相对简单,但是我在使用Scala / JavaFX时遇到了问题。

我在OS / X 10.8.4上使用Scala 2.10.2,JavaFX 2.2.0-b21和Java 1.7.0_06 Java HotSpot(TM)64位服务器VM。

graphicsContext2D.globalBlendMode = BlendMode.DIFFERENCE似乎等同于Graphics2D.setXORMode(),它几乎可以工作,但它:

  • 有时会留下填充矩形时矩形所在位置的微弱痕迹。
  • 生成灰色线条,除非线条宽度为偶数,否则在划线矩形时不会取纹。
  • 有时会在抚摸一个线宽为偶数的矩形时留下矩形所在位置的微弱痕迹。
  • 与父组件提供的背景无法正常异或。

最后一项不是我的预期,但我想我明白它在做什么(将Canvas中的未定义背景视为黑色,以便在绘制时将XOR设置为白色,并在未绘制时将其设置为黑色,即使它看起来是绿色的。)

这是一个显示问题的测试用例:

import scalafx.application.JFXApp
import scalafx.scene.Scene
import scalafx.scene.paint.Color
import scalafx.Includes._
import scalafx.scene.canvas.{GraphicsContext, Canvas}
import scalafx.scene.layout.Pane
import scalafx.scene.input._
import scalafx.geometry.Rectangle2D
import scalafx.scene.transform.Affine
import scalafx.scene.effect.BlendMode

object Dragger {
  var startX: Double = 0.0
  var startY: Double = 0.0

  var oldRectangle: Rectangle2D = null


  def mouseReleased(event: MouseEvent) {
  }

  def mousePressed(event: MouseEvent) {
    startX = event.x
    startY = event.y
  }

  def mouseDragged(g2: GraphicsContext, event: MouseEvent) {
    if (oldRectangle != null)
      drawRectangle(g2, oldRectangle)

    val x0 = math.min(startX, event.x)
    val y0 = math.min(startY, event.y)
    val newRectangle = new Rectangle2D(x0, y0, math.abs(event.x - startX), math.abs(event.y - startY))

    drawRectangle(g2, newRectangle)

    oldRectangle = newRectangle
  }

  def drawRectangle(g2: GraphicsContext, r: Rectangle2D) {
    //g2.strokeRect(r.minX, r.minY, r.width, r.height)        // <--- stroke instead of fill for grey lines that don't undraw
    g2.fillRect(r.minX, r.minY, r.width, r.height)
  }
}

object Test extends JFXApp
{
  println("javafx.runtime.version: " + System.getProperties.get("javafx.runtime.version"))
  println("java.runtime.version:   " + System.getProperties.get("java.runtime.version"))

  stage = new JFXApp.PrimaryStage {
    title = "Hello Stage"
    width = 600
    height = 472
    scene = new Scene {
      fill = Color.LIGHTGREEN
      root = new Pane {
        content = new Canvas(600, 450) {
          graphicsContext2D.setStroke(Color.BLUE)
          graphicsContext2D.setFill(Color.BLUE)
          graphicsContext2D.fillRect(4, 4, 592, 442)
          graphicsContext2D.setTransform(new Affine)
          graphicsContext2D.globalBlendMode = BlendMode.DIFFERENCE
          graphicsContext2D.setStroke(Color.WHITE)
          graphicsContext2D.setFill(Color.WHITE)
          graphicsContext2D.setLineWidth(1)                   // <--- increase line width to 2 to fix stroked line undrawing

          onMouseDragged = (event: MouseEvent) => {
            Dragger.mouseDragged(graphicsContext2D, event)
          }

          onDragDetected = (event: MouseEvent) => {
            //Drag complete
          }

          onMousePressed  = (event: MouseEvent) =>  {
            Dragger.mousePressed(event)
          }

          onMouseReleased  = (event: MouseEvent) =>  {
            Dragger.mouseReleased(event)
          }
        }
      }
    }
  }
}

此屏幕截图显示了重复移动鼠标后出现的问题(通过划线和2像素线宽):

Screenshot

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

您可以使用JavaFX功能而不是使用矩形移动自己。 您可以使用矩形的setTranstalteX()setTranslateY()方法。请参阅Ensemble示例 - &gt; Graphics - &gt; Transforms - &gt; Translate中的Oracle示例。 这里还有The Ensemble的代码:

public class TranslateSample extends Application {

private void init(Stage primaryStage) {
    Group root = new Group();
    primaryStage.setResizable(false);
    primaryStage.setScene(new Scene(root, 230,220));


    //create 2 rectangles with different color
    Rectangle rect1 = new Rectangle(90, 90, Color.web("#ed4b00", 0.75));
    Rectangle rect2 = new Rectangle(90, 90, Color.web("#ed4b00", 0.5));

    //translate second one
    rect2.setTranslateX(140);

    // rectangle with adjustable translate
    Rectangle rect3 = new Rectangle(40, 130, 60, 60);
    rect3.setFill(Color.DODGERBLUE);
    rect3.setTranslateX(20);
    rect3.setTranslateY(10);

    //show the rectangles
    root.getChildren().addAll(rect2, rect1, rect3);

    //create arrow
    Polygon polygon = createArrow();
    polygon.setLayoutX(110);
    polygon.setLayoutY(30);
    polygon.setRotate(90);

    root.getChildren().addAll(polygon);
}

public static Polygon createArrow() {
    Polygon polygon = new Polygon(new double[]{
        7.5, 0,
        15, 15,
        10, 15,
        10, 30,
        5, 30,
        5, 15,
        0, 15
    });

    polygon.setFill(Color.web("#ff0900"));

    return polygon;

}

public double getSampleWidth() { return 230; }

public double getSampleHeight() { return 220; }

@Override public void start(Stage primaryStage) throws Exception {
    init(primaryStage);
    primaryStage.show();
}
public static void main(String[] args) { launch(args); }

}