Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8191107

JavaFX WritableImage needs renew instance, otherwise IOOBE occurs in refreshing

XMLWordPrintable

    • generic
    • generic

      FULL PRODUCT VERSION :
      I've confirmed:
      java 9.0.1
      java 1.8.0_144

      ADDITIONAL OS VERSION INFORMATION :
      I've confirmed this exception in:
      Windows 7 32bit
      Windows 7 64bit
      Windows 10 64bit
      MacOS 10.13.1

      EXTRA RELEVANT SYSTEM CONFIGURATION :
      This report has no relationship about network or some other devices.
      The reported bug occurs in PC independently.


      A DESCRIPTION OF THE PROBLEM :
      I'd like to realize a video player by using JavaFX, and I know there are several JavaFX video players with file loading, but I 'd like to watch the video from byte[] to JavaFX Image for some reasons.
      As long as I know, WritableImage and PixelWriter#setPixels() are the only way to realize what I want to.

      When I use a ImageView, there is no problems.
      However, when I use some ImageViews, so many exceptions such as NullPointerException, java.lang.IndexOutOfBoundsException occur in JavaFX.
      The exceptions are shown the below of this description.
      To avoid the exceptions, I found WritableImage must be renewed every time.
      I wonder if this is a bug of JavaFX or specification.

      Finally, I made a minimum bug occurring source code.
      So I'd like you to check by using that code.


      Exception example:
      Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException: Index -1 out-of-bounds for length 48
      at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
      at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
      at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
      at java.base/java.util.Objects.checkIndex(Objects.java:372)
      at java.base/java.util.ArrayList.get(ArrayList.java:440)
      at javafx.graphics/javafx.scene.Parent.updateCachedBounds(Parent.java:1723)
      at javafx.graphics/javafx.scene.Parent.recomputeBounds(Parent.java:1667)
      at javafx.graphics/javafx.scene.Parent.doComputeGeomBounds(Parent.java:1520)
      at javafx.graphics/javafx.scene.Parent.access$200(Parent.java:81)
      at javafx.graphics/javafx.scene.Parent$1.doComputeGeomBounds(Parent.java:117)
      at javafx.graphics/com.sun.javafx.scene.ParentHelper.computeGeomBoundsImpl(ParentHelper.java:86)
      at javafx.graphics/com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBoundsImpl(RegionHelper.java:78)
      at javafx.graphics/com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBounds(RegionHelper.java:62)
      at javafx.graphics/javafx.scene.layout.Region.doComputeGeomBounds(Region.java:3291)
      at javafx.graphics/javafx.scene.layout.Region.access$300(Region.java:149)
      at javafx.graphics/javafx.scene.layout.Region$1.doComputeGeomBounds(Region.java:170)
      at javafx.graphics/com.sun.javafx.scene.layout.RegionHelper.computeGeomBoundsImpl(RegionHelper.java:89)
      at javafx.graphics/com.sun.javafx.scene.NodeHelper.computeGeomBounds(NodeHelper.java:117)
      at javafx.graphics/javafx.scene.Node.updateGeomBounds(Node.java:3831)
      at javafx.graphics/javafx.scene.Node.getGeomBounds(Node.java:3793)
      at javafx.graphics/javafx.scene.Node.getLocalBounds(Node.java:3741)
      at javafx.graphics/javafx.scene.Node.updateTxBounds(Node.java:3895)
      at javafx.graphics/javafx.scene.Node.getTransformedBounds(Node.java:3687)
      at javafx.graphics/javafx.scene.Node.updateBounds(Node.java:766)
      at javafx.graphics/javafx.scene.Parent.updateBounds(Parent.java:1851)
      at javafx.graphics/javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2522)
      at javafx.graphics/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:410)
      at java.base/java.security.AccessController.doPrivileged(Native Method)
      at javafx.graphics/com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:409)
      at javafx.graphics/com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:436)
      at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:518)
      at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:498)
      at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:491)
      at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:319)
      at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)


      REGRESSION. Last worked in version 8u144

      ADDITIONAL REGRESSION INFORMATION:
      Java version "1.8.0_144"
      Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
      Java HotSpot (TM) 64-Bit Server VM (build 25.144-b01, mixed mode)


      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. WritableImage instance is initialized
       WritableImage wi = new WritableImage(width, height)

      2. fill in the image data into byte[]
       byte[] data = fillInData()

      3. write pixel data from byte[] into WritableImage
       wi.getPixelWriter().setPixels(0, 0, width, height, pf, data, 0, width*3)

      4. set WritableImage to ImageView
       imageView.setImage(wi)

      5. loop from 2


      I got an error in step 4.
      The content of error depends on a case.
      For example, like below:
      Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException: Index -1 out-of-bounds for length 48
      at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
      at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
      at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
      at java.base/java.util.Objects.checkIndex(Objects.java:372)
      at java.base/java.util.ArrayList.get(ArrayList.java:440)
      at javafx.graphics/javafx.scene.Parent.updateCachedBounds(Parent.java:1723)
      at javafx.graphics/javafx.scene.Parent.recomputeBounds(Parent.java:1667)
      at javafx.graphics/javafx.scene.Parent.doComputeGeomBounds(Parent.java:1520)
      at javafx.graphics/javafx.scene.Parent.access$200(Parent.java:81)
      at javafx.graphics/javafx.scene.Parent$1.doComputeGeomBounds(Parent.java:117)
      at javafx.graphics/com.sun.javafx.scene.ParentHelper.computeGeomBoundsImpl(ParentHelper.java:86)
      at javafx.graphics/com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBoundsImpl(RegionHelper.java:78)
      at javafx.graphics/com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBounds(RegionHelper.java:62)
      at javafx.graphics/javafx.scene.layout.Region.doComputeGeomBounds(Region.java:3291)
      at javafx.graphics/javafx.scene.layout.Region.access$300(Region.java:149)
      at javafx.graphics/javafx.scene.layout.Region$1.doComputeGeomBounds(Region.java:170)
      at javafx.graphics/com.sun.javafx.scene.layout.RegionHelper.computeGeomBoundsImpl(RegionHelper.java:89)
      at javafx.graphics/com.sun.javafx.scene.NodeHelper.computeGeomBounds(NodeHelper.java:117)
      at javafx.graphics/javafx.scene.Node.updateGeomBounds(Node.java:3831)
      at javafx.graphics/javafx.scene.Node.getGeomBounds(Node.java:3793)
      at javafx.graphics/javafx.scene.Node.getLocalBounds(Node.java:3741)
      at javafx.graphics/javafx.scene.Node.updateTxBounds(Node.java:3895)
      at javafx.graphics/javafx.scene.Node.getTransformedBounds(Node.java:3687)
      at javafx.graphics/javafx.scene.Node.updateBounds(Node.java:766)
      at javafx.graphics/javafx.scene.Parent.updateBounds(Parent.java:1851)
      at javafx.graphics/javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2522)
      at javafx.graphics/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:410)
      at java.base/java.security.AccessController.doPrivileged(Native Method)
      at javafx.graphics/com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:409)
      at javafx.graphics/com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:436)
      at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:518)
      at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:498)
      at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:491)
      at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:319)
      at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)



      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      To avoid an error, before step 3 in above, renew WritableImage instance.
       wi = new WritableImage(width, height)

      My expected result is no need to renew.
      Or is this a specification of JavaFX?
      ACTUAL -
      The exception example:
      Exception in thread "JavaFX Application Thread" java.lang.IndexOutOfBoundsException: Index -1 out-of-bounds for length 48
      at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
      at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
      at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
      at java.base/java.util.Objects.checkIndex(Objects.java:372)
      at java.base/java.util.ArrayList.get(ArrayList.java:440)
      at javafx.graphics/javafx.scene.Parent.updateCachedBounds(Parent.java:1723)
      at javafx.graphics/javafx.scene.Parent.recomputeBounds(Parent.java:1667)
      at javafx.graphics/javafx.scene.Parent.doComputeGeomBounds(Parent.java:1520)
      at javafx.graphics/javafx.scene.Parent.access$200(Parent.java:81)
      at javafx.graphics/javafx.scene.Parent$1.doComputeGeomBounds(Parent.java:117)
      at javafx.graphics/com.sun.javafx.scene.ParentHelper.computeGeomBoundsImpl(ParentHelper.java:86)
      at javafx.graphics/com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBoundsImpl(RegionHelper.java:78)
      at javafx.graphics/com.sun.javafx.scene.layout.RegionHelper.superComputeGeomBounds(RegionHelper.java:62)
      at javafx.graphics/javafx.scene.layout.Region.doComputeGeomBounds(Region.java:3291)
      at javafx.graphics/javafx.scene.layout.Region.access$300(Region.java:149)
      at javafx.graphics/javafx.scene.layout.Region$1.doComputeGeomBounds(Region.java:170)
      at javafx.graphics/com.sun.javafx.scene.layout.RegionHelper.computeGeomBoundsImpl(RegionHelper.java:89)
      at javafx.graphics/com.sun.javafx.scene.NodeHelper.computeGeomBounds(NodeHelper.java:117)
      at javafx.graphics/javafx.scene.Node.updateGeomBounds(Node.java:3831)
      at javafx.graphics/javafx.scene.Node.getGeomBounds(Node.java:3793)
      at javafx.graphics/javafx.scene.Node.getLocalBounds(Node.java:3741)
      at javafx.graphics/javafx.scene.Node.updateTxBounds(Node.java:3895)
      at javafx.graphics/javafx.scene.Node.getTransformedBounds(Node.java:3687)
      at javafx.graphics/javafx.scene.Node.updateBounds(Node.java:766)
      at javafx.graphics/javafx.scene.Parent.updateBounds(Parent.java:1851)
      at javafx.graphics/javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2522)
      at javafx.graphics/com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:410)
      at java.base/java.security.AccessController.doPrivileged(Native Method)
      at javafx.graphics/com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:409)
      at javafx.graphics/com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:436)
      at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:518)
      at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:498)
      at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:491)
      at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:319)
      at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)



      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      there is no crash log.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.nio.ByteBuffer;
      import java.util.ArrayList;

      import javafx.application.Application;
      import javafx.concurrent.Task;
      import javafx.scene.Scene;
      import javafx.scene.control.Label;
      import javafx.scene.image.Image;
      import javafx.scene.image.ImageView;
      import javafx.scene.image.PixelFormat;
      import javafx.scene.image.WritableImage;
      import javafx.scene.layout.BorderPane;
      import javafx.scene.layout.VBox;
      import javafx.stage.Stage;

      /**
       * WritableImage test code
       */
      public class WritableImageTest extends Application {
      // If false, some Exception occurs and GUI refresh stops.
      // To avoid Exception, this flag must be set to true.
      boolean exceptionAvoidFlag = false;
      @Override
      public void start(Stage primaryStage) throws Exception {
      VBox vBox = new VBox();
      ArrayList<UpdateTask> tasks = new ArrayList<>();
      for (int i = 0; i < 50; i++) {
      UpdateTask task = new UpdateTask(i);
      tasks.add(task);
      vBox.getChildren().add(createBp(task));
      }
      tasks.forEach(t -> new Thread(t).start());
      Scene scene = new Scene(vBox, 400, 600);
      primaryStage.setScene(scene);
      primaryStage.setOnCloseRequest(v -> tasks.forEach(t -> t.stop = true));
      primaryStage.show();
      }

      /**
      * create BorderPane with Label and ImageView
      */
      BorderPane createBp(UpdateTask task) {
      final BorderPane borderPane = new BorderPane();
      final Label label = new Label("update info...");
      final ImageView imageView = new ImageView();
      task.valueProperty().addListener((ob, o, n) -> {
      if (n == null) return;
      label.setText(n.labelStr);
      imageView.setImage(n.image);
      });
      borderPane.setTop(label);
      borderPane.setCenter(imageView);;
      return borderPane;
      }

      /**
      * main entry point
      */
      public static void main(String args[]) {
      launch(args);
      }

      /**
      * update label info and image
      */
      class UpdateTask extends Task<UpdateInfo> {
      boolean stop = false;
      int id;
      int width = 320, height = 240;
      public UpdateTask(int _id) {
      id = _id;
      }
      @Override
      protected UpdateInfo call() throws Exception {
      int ctr = 0;
      byte[] data = new byte[width * height * 3];
      WritableImage wi = new WritableImage(width, height);
      PixelFormat<ByteBuffer> pf = PixelFormat.getByteRgbInstance();
      while (!stop) {
      String str = id+" update: "+ctr;
      // TODO need to renew WritableImage instance in order to avoid NLE
      if (exceptionAvoidFlag) wi = new WritableImage(width, height);
      fillImage(wi, pf, ctr++, data);
      updateValue(new UpdateInfo(str, wi));
      Thread.sleep(50);
      }
      return null;
      }

      /**
      * fill in arbitrary data and write into WritableImage
      */
      private void fillImage(WritableImage wi, PixelFormat<ByteBuffer> pf, int index, byte[] data) {
      for (int i = 0; i < data.length; i+=3) {
      data[i + 0] = (byte) (i + index * 3);
      data[i + 1] = (byte) (i + index * 3);
      data[i + 2] = (byte) (i + index * 3);
      }
      wi.getPixelWriter().setPixels(0, 0, width, height, pf, data, 0, width * 3);
      }
      }

      /**
      * update info
      */
      class UpdateInfo {
      String labelStr;
      Image image;
      public UpdateInfo(String s, Image i) {
      labelStr = s;
      image = i;
      }
      }

      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Same as Expected Result.

            arapte Ambarish Rapte
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated: