2015-09-04 84 views
0

给出BufferedImage中的真彩色全帧列表以及帧持续时间列表,我如何创建一个无损图像,当放在JLabel上时,它将生成动画?如何从静帧创建动画图像?

从我可以find,我可以创建一个ImageWriter包装一个ByteArrayOutputStream,写IIOImage帧,然后Toolkit.getDefaultToolkit().createImage流成ToolkitImage

这个尝试有两个问题。

  • 的ImageWriter只能与已知的图像编码器中的一个实例,并且是没有的,一种对无损真彩色动画图像格式(例如MNG),
  • 它编码(压缩)的图像,然后解压缩它再次成为不必要的性能危害。

[编辑]
一些更简洁的约束和要求。请不要拿出任何弯曲这些规则的东西。

想:

  • 制作的动画线程和绘画/更新动画自己的每一帧,
  • 使用任何类型的第三方库的,
  • 借款的任何外部过程,例如网页浏览器,
  • 将其显示在某种视频播放器对象或3D加速场景(OpenGL/etc)中,
  • 工作直接LY从sun.*

想类:

  • 帧大小可以作为显示器尺寸一样大。请不要担心表演。我会担心的。你只会担心正确性。
  • 框架都具有相同的尺寸,
  • Image子类。我应该能够绘制像g.drawImage(ani, 0, 0, this)这样的图像,并且它会生成动画,或者将其包含在ImageIcon中并将其显示在JLabel/JButton /等上,并且它将生成动画,每个帧可以具有不同的延迟,从10ms直到第二,
  • 动画可以循环或可以结束了,这是每个动画(就像GIF)定义一次,
  • 我可以使用任何与Oracle的Java 8(如JavaFX的)包装,
  • 不管发生什么,应该与SWING集成

可选:

  • 框架可以具有透明度。如果需要,我可以预先将图像不透明化,因为无论如何,动画将显示在已知背景(单色)上。
  • 我不在乎自己是否要继承Image,并在那里添加一个动画线程,该线程将与ImageObserver合作,或者编写我自己的InputStreamImageSource,但我不知道如何。
  • 如果我能以某种方式显示一个带有一些HTML和CSS代码的JavaFX场景来激活我的图像,那也没关系。但只要它全部封装在一个可以传递的SWING兼容对象中。
+1

我不会使用JLabel。我会使用paintComponent方法直接在JPanel上绘制图像。你的图像有多大? –

+0

图像大小可以是任何图标到监视器分辨率(不大)。我实际上并不需要JLabel(我将不得不重写一个当前使用JLabel的应用程序)。但主要是,我要求JLabel兼容性是一种简单的方法:一个“(Toolkit)映像”,其中包含所有帧。 –

+0

使用不同尺寸的图片,动画更加刺激。您希望每个图像显示多长时间? –

回答

0

public void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    g.drawImage(getImageForCurrentTime(), 3, 4, this); 
} 

+0

如果我只想在自己的动画循环中绘制框架,我不需要在这里问。 –

+0

然后我错过了一些东西,我不明白为什么这个解决方案是不可接受的,你只需要检查System.currentTimeInMilis()并且有一些逻辑来决定你想要加载哪个图像 – sherif

1

你说得对,ImageIO的是不是一种选择,为支持保证是GIF唯一的动画格式。

你说你不想制作一个动画线程,但是一个JavaFX动画对象怎么样,比如Timeline

public JComponent createAnimationComponent(List<BufferedImage> images, 
              List<Long> durations) { 

    Objects.requireNonNull(images, "Image list cannot be null"); 
    Objects.requireNonNull(durations, "Duration list cannot be null"); 

    if (new ArrayList<Object>(images).contains(null)) { 
     throw new IllegalArgumentException("Null image not permitted"); 
    } 
    if (new ArrayList<Object>(durations).contains(null)) { 
     throw new IllegalArgumentException("Null duration not permitted"); 
    } 

    int count = images.size(); 
    if (count != durations.size()) { 
     throw new IllegalArgumentException(
      "Lists must have the same number of elements"); 
    } 

    ImageView view = new ImageView(); 
    ObjectProperty<Image> imageProperty = view.imageProperty(); 

    Rectangle imageSize = new Rectangle(); 
    KeyFrame[] frames = new KeyFrame[count]; 
    long time = 0; 
    for (int i = 0; i < count; i++) { 
     Duration duration = Duration.millis(time); 
     time += durations.get(i); 

     BufferedImage bufImg = images.get(i); 
     imageSize.add(bufImg.getWidth(), bufImg.getHeight()); 

     Image image = SwingFXUtils.toFXImage(bufImg, null); 
     KeyValue imageValue = new KeyValue(imageProperty, image, 
      Interpolator.DISCRETE); 
     frames[i] = new KeyFrame(duration, imageValue); 
    } 

    Timeline timeline = new Timeline(frames); 
    timeline.setCycleCount(Animation.INDEFINITE); 
    timeline.play(); 

    JFXPanel panel = new JFXPanel(); 
    panel.setScene(new Scene(new Group(view))); 
    panel.setPreferredSize(imageSize.getSize()); 

    return panel; 
} 

(我不知道为什么有必要明确地设置JFXPanel的首选大小,但它是。可能是一个错误。)

注意像所有的JavaFX代码,它必须运行,在JavaFX应用程序线程中。如果你使用的是Swing应用程序,你可以这样做:

public JComponent createAnimationComponentFromAWTThread(
           final List<BufferedImage> images, 
           final List<Long> durations) 
throws InterruptedException { 

    final JComponent[] componentHolder = { null }; 

    Platform.runLater(new Runnable() { 
     @Override 
     public void run() { 
      synchronized (componentHolder) { 
       componentHolder[0] = 
        createAnimationComponent(images, durations); 
       componentHolder.notifyAll(); 
      } 
     } 
    }); 

    synchronized (componentHolder) { 
     while (componentHolder[0] == null) { 
      componentHolder.wait(); 
     } 
     return componentHolder[0]; 
    } 
} 

但这还不够。您首先必须通过调用Application.launch来初始化JavaFX,无论是使用显式方法调用还是通过将Application子类指定为主类来隐式地调用。

+0

我不得不解决两个小问题,之后它就起作用了!首先使用'new JFXPanel()'初始化JavaFX线程,并在时间轴的末尾添加第一个图像,最后的时间戳为 –

+0

好吧,目前不能与SWING集成。当我做'frame.addMouseListener(x)'和'frame.setContentPane(thatJFXPanel)',我的鼠标事件不会通过。任何想法,如果有一个坚实的修复(不只是鼠标事件的黑客攻击,但要完全和完全像JComponent行为?(我似乎无法使它透明) –

+0

我怀疑它,我怀疑你可以解决JLayer的鼠标事件问题,但由于JavaFX,而不是Swing,负责JFXPanel的内容,我怀疑它可以支持所有的AWT/Swing功能。 – VGR