2015-09-04 3 views
0

Учитывая список полноцветных полноразмерных кадров в BufferedImage и список длительностей кадров, как я могу создать изображение без потерь, что при надевании JLabel будет анимироваться?Как создать анимированное изображение из неподвижных кадров?

Из того, что я могу find, я мог бы создать ImageWriter Обертывание ByteArrayOutputStream, написать IIOImage кадры к нему, а затем Toolkit.getDefaultToolkit().createImage поток в ToolkitImage.

Есть две проблемы с этой попыткой.

  • ImageWriter может быть обработан только с одним из известных датчиков изображения, и не существует ни для формата анимированных изображений без потерь истинного цвета (например, MNG),
  • Он кодирует (сжимает) изображение, а затем распаковывает это снова становится ненужной угрозой для работы.

[Edit]
Некоторые более краткие ограничения и требования. Пожалуйста, не придумывайте ничего, что сгибает эти правила.

Что я не Требуется:

  • Создание нить анимации и живописи/обновления каждого кадра анимации сам,
  • Использование любого рода 3-сторонней библиотеки,
  • Заимствование любой внешний процесс, например, веб-браузер,
  • Показать его в виде видеопроигрывателя или сцены с 3D-ускорением (OpenGL/etc),
  • Работайте напрямую LY с классами из sun.* пакетов

Что сделать хотите:

  • Размер кадра может быть столь же большим, как размер монитора. Пожалуйста, не беспокойтесь о производительности. Я буду беспокоиться об этом. Вы просто будете беспокоиться о правильности.
  • Рамы все имеют одинаковый размер,
  • a Image подкласс. Я должен был бы нарисовать изображение как g.drawImage(ani, 0, 0, this), и он будет анимировать или обернуть его в ImageIcon и отобразить его на JLabel/JButton/etc, и он будет анимировать,
  • Каждый кадр может иметь различную задержку от 10 мс до секунды,
  • Анимация может цикл или может закончиться, и это определяется один раз в анимации (так же, как GIF),
  • я могу использовать что-нибудь упакованного с Oracle Java 8 (например, JavaFX),
  • Что бы ни случилось , он должен интегрироваться с SWING

Дополнительно:

  • Рамки могут иметь прозрачность.Если необходимо, я могу заранее опасаться изображениям, так как анимация будет показана на известном фоне (один цвет).
  • Мне все равно, должен ли я сам подкласс Image и добавить туда анимационную нить, которая будет сотрудничать с ImageObserver или написать свой собственный InputStreamImageSource, но я не знаю как.
  • Если я могу каким-то образом показать сцену JavaFX с некоторым кодом HTML и CSS, который оживляет мои изображения, то это тоже хорошо. НО до тех пор, пока все они инкапсулированы в один SWING-совместимый объект, который я могу пройти.
+1

Я бы не использовал JLabel. Я бы рисовал изображения непосредственно на JPanel, используя метод paintComponent. Насколько велики ваши образы? –

+0

Размер изображения может быть любым: от значка до разрешения монитора (не больше). На самом деле я не требую JLabel (мне придется переписать одно из моих приложений, которое в настоящее время использует JLabel). Но в основном я попросил JLabel совместимость как простой способ означать: один '(Toolkit) Image', который содержит все кадры , –

+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 Application Thread. Если вы используете его из приложения 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]; 
    } 
} 

Но этого все еще недостаточно. Сначала вам нужно инициализировать JavaFX, вызвав Application.launch либо с явным вызовом метода, либо неявно, указав подкласс приложения как основной класс.

+0

Мне пришлось исправить две небольшие серьезные проблемы с этим , а после этого он сработал! Сначала инициализируйте поток JavaFX с помощью 'new JFXPanel();' и добавьте первое изображение в конце временной шкалы с окончательной отметкой времени –

+0

Хорошо, в настоящее время НЕ интегрируется с SWING. Когда я делаю 'frame.addMouseListener (x)' и 'frame.setContentPane (thatJFXPanel)', мои события мыши не проходят. Любая идея, если есть твердое исправление (а не просто взломать события мыши, но сделать это полностью и полностью вести себя как JComponent?) (Я тоже не могу сделать его прозрачным) –

+0

Я сомневаюсь. Я подозреваю, что вы можете решить проблема с событием мыши с JLayer, но так как JavaFX, а не Swing, отвечает за содержимое JFXPanel, я сомневаюсь, что это можно сделать для поддержки всех возможностей AWT/Swing. – VGR

Смежные вопросы