title: JavaFX rendering / image manipulation
body: <p>I made a little JavaFX app generating longshadows. At this point I struggle with the rendering (see picture). </p>
  
  <ol>
  <li>The missing line on the rectangle's corner seems hard to fix. Changing the loop, which applies the manipulation, will mess up other shapes' shadow (e.g. circle). </li>
  <li>The glitch at 'a' is related to the <a href="https://en.wikipedia.org/wiki/Bresenham&#39;s_line_algorithm" rel="nofollow noreferrer">Bresenham algorithm</a>, I guess.(?) </li>
  </ol>
  
  <h3>Additional info:</h3>
  
  <p>Changing the image resolution makes no difference: Gitches keep showing. </p>
  
  <h3>Question:</h3>
  
  <p>How to get it fixed? Does the SDK provide something helpful? Do I have to rewrite the code?</p>
  
  <p><img src="https://i.stack.imgur.com/1ZTSc.png" alt="Screenshot"></p>
  
  <h3>Code</h3>
  
  <pre><code>import java.awt.image.BufferedImage;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.PrintWriter;
  
  import javax.imageio.ImageIO;
  
  import javafx.application.Application;
  import javafx.geometry.Pos;
  import javafx.scene.Scene;
  import javafx.scene.canvas.Canvas;
  import javafx.scene.canvas.GraphicsContext;
  import javafx.scene.control.Label;
  import javafx.scene.image.Image;
  import javafx.scene.image.ImageView;
  import javafx.scene.image.PixelWriter;
  import javafx.scene.input.KeyCode;
  import javafx.scene.input.KeyEvent;
  import javafx.scene.input.MouseEvent;
  import javafx.scene.layout.StackPane;
  import javafx.scene.paint.Color;
  import javafx.scene.paint.Paint;
  import javafx.stage.Stage;
  
  
  public class Main extends Application {
  
      private PrintWriter writer;
  
      private String colorObjFilter = "0x009688ff";
  
      private static final String IMG_PATH = "img/ls-test-1k.png";
  
      private static final int LONGSHADOW_LENGTH = 100;
  
      private static final String
              ANSI_GREEN = "\u001B[32m",
              ANSI_RESET = "\u001B[0m";
  
  
      @Override
      public void start(Stage stage) throws Exception {
          writer = new PrintWriter("out.txt", "UTF-8");
  
          InputStream is = new FileInputStream(new File(IMG_PATH));
          ImageView imageView = new ImageView(new Image(is));
          Image image = imageView.getImage();
  
          StackPane root = new StackPane();
          Scene scene = new Scene(root, image.getWidth(), image.getHeight(), Paint.valueOf
                  ("#EEEEEE"));
          scene.addEventFilter(KeyEvent.KEY_PRESSED, evt -&gt; {
              if (evt.getCode().equals(KeyCode.ESCAPE)) {
                  stage.close();
              }
          });
  
          final Canvas canvas = new Canvas(image.getWidth(), image.getHeight());
  
          canvas.setOnMouseClicked((MouseEvent e) -&gt; {
              Color color = image.getPixelReader().getColor((int) e.getX(), (int) e.getY());
              System.out.println(ANSI_GREEN + " -&gt; " + color.toString() + ANSI_RESET);
              colorObjFilter = color.toString();
  
              try {
                  processImage(root, canvas, image);
              } catch (IOException e1) {
                  e1.printStackTrace();
              }
          });
  
          root.getChildren().addAll(imageView, canvas);
          stage.setScene(scene);
          stage.show();
      }
  
  
      private void processImage(StackPane root, Canvas canvas, Image image) throws IOException {
          long delta = System.currentTimeMillis();
  
          int width = (int) image.getWidth();
          int height = (int) image.getHeight();
  
          GraphicsContext gc = canvas.getGraphicsContext2D();
          System.out.println("width: " + width + "\theight: " + height);
  
          BufferedImage bufferedImage = ImageIO.read(new File(IMG_PATH));
  
          // keep threshold small to get clean paths to draw
          edgeDetection(gc, image, 0.00000001d);
  
          writer.close();
          Label label = new Label();
          root.setAlignment(Pos.BOTTOM_LEFT);
          root.setOnMouseMoved(event -&gt; label.setText(event.getX() + "|" + event.getY()
                  + "|" + bufferedImage.getRGB((int) event.getX(), (int) event.getY())));
          root.getChildren().addAll(label);
          System.out.println("took: " + (System.currentTimeMillis() - delta) + " ms");
  
      }
  
  
      public void edgeDetection(GraphicsContext gc, Image image, double threshold) {
          Color topPxl, lowerPxl;
          double topIntensity, lowerIntensity;
          PixelWriter pw = gc.getPixelWriter();
  
          for (int y = 0; y &lt; image.getHeight() - 1; y++) {
              for (int x = 1; x &lt; image.getWidth(); x++) {
  
                  topPxl = image.getPixelReader().getColor(x, y);
                  lowerPxl = image.getPixelReader().getColor(x - 1, y + 1);
  
                  topIntensity = (topPxl.getRed() + topPxl.getGreen() + topPxl.getBlue()) / 3;
                  lowerIntensity = (lowerPxl.getRed() + lowerPxl.getGreen() + lowerPxl.getBlue()) / 3;
  
                  if (Math.abs(topIntensity - lowerIntensity) &gt; threshold) {
                      int y2 = y;
                      for (int x2 = x; x2 &lt; x + LONGSHADOW_LENGTH; x2++) {
                          y2++;
                          try {
                              Color color = image.getPixelReader().getColor(x2, y2);
                              // colorObjFilter protects the purple letter being manipulated
                              if (!color.toString().toLowerCase()
                                      .contains(colorObjFilter.toLowerCase())) {
                                  pw.setColor(x2, y2, Color.color(.7f, .7f, .7f, .9f));
                              }
                          } catch (Exception e) {
                              System.out.println("Error: " + e.getMessage());
                          }
                      }
                  }
              }
          }
      }
  
  
      public static void main(String[] args) {
          launch(args);
      }
  }
  </code></pre>