简体   繁体   中英

How do I solve drawing XOR artefact problems in ScalaFX/JavaFX with BlendMode.Difference?

I'm porting some code from Java to Scala and having problems with drawing artefacts when attempting "rubber banding" - ie drawing a rectangle that moves with the mouse pointer.

This was relatively simple to do in Java2D, but I'm having problems making it work in Scala/JavaFX.

I'm using Scala 2.10.2, JavaFX 2.2.0-b21 and , Java 1.7.0_06 Java HotSpot(TM) 64-Bit Server VM on OS/X 10.8.4.

graphicsContext2D.globalBlendMode = BlendMode.DIFFERENCE seems equivalent to Graphics2D.setXORMode() and it almost works, but it:

  • sometimes leaves faint traces of where the rectangle has been when filling a rectangle.
  • produces grey lines that don't undraw when stroking a rectangle unless the line width is an even integer.
  • sometimes leaves faint traces of where the rectangle has been when stroking a rectangle with a line width that is an even integer.
  • doesn't XOR properly with the background provided by the parent component.

The last item isn't what I expected, but I think I understand what it is doing (treating the undefined background in the Canvas as black, so that it XORs to white on draw, and black on undraw, even though it looked green to start with.)

This is a test case that shows the problem:

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

This screenshot shows the problem (this is with stroking and a 2 pixel line width) after moving the mouse around repeatedly:

屏幕截图

Any help would be greatly appreciated.

You can use the JavaFX capabilities instead of doing the rectangle move your self. you can use the setTranstalteX() and setTranslateY() method of the rectangle. see Oracle example in the Ensemble Sample-->Graphics-->Transforms-->Translate. Here also the code from 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); }

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM