Ich habe bereits vor geraumer Zeit einen Artikel zum Thema Erzeugung von AnimGifs mittels Java veröffentlicht - hier erkläre ich nun, wie ich auf die Idee gekommen bin...
Ich bin in meiner Freizeit - und manchmal auf dem Weg zur Arbeit oder nach Hause - viel auf Mastodon - oder wie man heute moderner sagt: im Fediverse - unterwegs. Dort findet man immer wieder Beispiele generativer Programmierung, die kleine Kunstwerke ergibt.
Viele der dort zu findenden Beispiele wurden mittels Processing erstellt - und es sind häufig AnimGIFs, die hier geteilt werden. Daher also mein ursprüngliches Interesse an der Möglichkeit, Multimediainhalte in dieser Form mittels Java zu erstellen.
Mein letzter Ausflug in Richtung Programmierung und Erzeugung von Computergraphik war eine Vorstufe davon. Ich wollte aber mit Processing darüber hinaus gehen: Mir schwebte eine Komponente vor, die ich in eigene Anwendungen einbauen könnte, um damit meine Idee zu vervollkommnen.
Die Beschäftigung mit AnimGIFs resultierte unter anderem aus einem der zahlreichen sogenannten Templates zur Erzeugung von Animationen für Processing - wenn man das einfach nachnutzen könnte, wäre das schon toll.
Ich musste allerdings feststellen, dass dieses Template gar kein AnimGIF erzeugt sondern einfach nur eine Menge einzelner Bilder, die man hinterher erst noch zu einem AnimGIF konvertieren muss. Das musste doch auch anders gehen?
Ich fand processing-java und ging als nächstes auf die Suche nach einer Variante, echte .pde Dateien direkt in Java nutzen zu können - leider war dazu nichts zu finden. Man muss - meinem aktuellen Kenntnisstand zufolge - die Sourcen mittels Processing in Java-Quelltexte umwandeln und diese entstehenden Klassen kann man dann in Java benutzen. Und so ergibt die Umwandlung des Beispiels folgenden Code:
/* autogenerated by Processing revision 1293 on 2024-11-20 */
import processing.core.*;
import processing.data.*;
import processing.event.*;
import processing.opengl.*;
import java.util.HashMap;
import java.util.ArrayList;
import java.io.File;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
public class fractalsquareorbits extends PApplet {
// Processing code by Etienne Jacob
// motion blur template by beesandbombs, explanation/article: https://bleuje.com/tutorial6/
// See the license information at the end of this file.
//////////////////////////////////////////////////////////////////////////////
// Start of template
int[][] result; // pixel colors buffer for motion blur
float t; // time global variable in [0,1[
float c; // other global variable for testing things, controlled by mouse
//-----------------------------------
// ease in and out, [0,1] -> [0,1], with a parameter g:
// https://patakk.tumblr.com/post/88602945835/heres-a-simple-function-you-can-use-for-easing
public float ease(float p, float g) {
if (p < 0.5f)
return 0.5f * pow(2*p, g);
else
return 1 - 0.5f * pow(2*(1 - p), g);
}
//-----------------------------------
public void draw()
{
if (!recording) // test mode...
{
t = (mouseX*1.3f/width)%1;
c = mouseY*1.0f/height;
if (mousePressed)
println(c);
draw_();
}
else // render mode...
{
for (int i=0; i<width*height; i++)
for (int a=0; a<3; a++)
result[i][a] = 0;
c = 0;
for (int sa=0; sa<samplesPerFrame; sa++) {
t = map(frameCount-1 + sa*shutterAngle/samplesPerFrame, 0, numFrames, 0, 1);
t %= 1;
draw_();
loadPixels();
for (int i=0; i<pixels.length; i++) {
result[i][0] += red(pixels[i]);
result[i][1] += green(pixels[i]);
result[i][2] += blue(pixels[i]);
}
}
loadPixels();
for (int i=0; i<pixels.length; i++)
pixels[i] = 0xff << 24 |
PApplet.parseInt(result[i][0]*1.0f/samplesPerFrame) << 16 |
PApplet.parseInt(result[i][1]*1.0f/samplesPerFrame) << 8 |
PApplet.parseInt(result[i][2]*1.0f/samplesPerFrame);
updatePixels();
if (frameCount<=numFrames) {
saveFrame("data/fr println(frameCount,"/",numFrames);
}
if (frameCount==numFrames)
stop();
}
}
// End of template
//////////////////////////////////////////////////////////////////////////////
// ... actual code omitted
}
Hier sieht man auch die Methode saveFrame, die wie beschrieben die Einzelbilder speichert.
Zur direkten Erzeugung des AnimGIF als Resultat musste ich nun lediglich den erzeugten Code ein wenig ergänzen:
Animator animator;
public void saveFrame(java.lang.String label)
{
PImage target =
createImage(width,height, BufferedImage.TYPE_INT_ARGB);
if (target != null)
{
int count = PApplet.min(pixels.length, target.pixels.length);
System.arraycopy(pixels, 0, target.pixels, 0, count);
animator.ch.put(de.elbosso.ui.image.Utilities.toBufferedImage(target.getImage(),BufferedImage.TYPE_INT_ARGB));
}
}
static class Animator extends java.lang.Thread implements java.util.Iterator<java.awt.image.RenderedImage>
{
de.netsysit.util.threads.CubbyHole<java.awt.image.RenderedImage> ch=new SimpleBlockingCubbyHole();
Animator()
{
}
@Override
public void run()
{
try
{
de.elbosso.ui.image.Utilities.writeAnimGif(23,2,this,new File("/tmp/te.gif"));
} catch (IOException e)
{
throw new RuntimeException(e);
}
}
@Override
public boolean hasNext()
{
return true;
}
@Override
public RenderedImage next()
{
try
{
return ch.get();
} catch (InterruptedException e)
{
throw new RuntimeException(e);
}
}
}
Dieser Code wird durch die Ergänzung in der Methode setup() aktiviert:
public void setup()
{
//other setup stuff goes here
//activate AnimGIF creator
animator=new Animator();
animator.start();
}
Und schließlich der Vollständigkeit halber hier die Methode, um einen Processing-Sketch in einer Java-Anwendung als Swing-Widget zu nutzen:
public static void main(String... args)
{
// create your JFrame
JFrame frame = new JFrame("JFrame Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// create your sketch
fractalsquareorbits pt = new fractalsquareorbits();
// get the PSurface from the sketch
PSurface ps = pt.initSurface();
// initialize the PSurface
ps.setSize(800, 800);
// get the SmoothCanvas that holds the PSurface
PSurfaceAWT.SmoothCanvas smoothCanvas = (PSurfaceAWT.SmoothCanvas) ps.getNative();
// SmoothCanvas can be used as a Component
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(new Label("Quark was here."), BorderLayout.NORTH);
panel.add(smoothCanvas, BorderLayout.CENTER);
frame.add(panel);
// Make the window visible
frame.setSize(800, 800);
frame.setVisible(true);
panel.invalidate();
panel.validate();
panel.doLayout();
frame.pack();
// start your sketch
ps.startThread();
}
07.02.2025
Da ich mich in den letzten Wochen wieder einmal mit Javas Sicherheitsmechanismen und dem Erzeugen von Animationen beschäftigt habe, habe ich den Entschluss gefasst, die bisher mittels JMF AVIs in dWb+ zu erstetzen - nur wodurch?
07.02.2025
Ich hatte neulich hier einen Link zu Multi-Scale Truchet Patterns und habe seitdem den Algorithmus mit java umgesetzt und ihn als Teil meines Projekts zur Testdatengenerierung veröffentlicht.
OAuth und OTP
16.02.2020
Wie bereits beschrieben will ich mich demnächst näher mit OAuth befassen...
Weiterlesen...Android Basteln C und C++ Chaos Datenbanken Docker dWb+ ESP Wifi Garten Geo Go GUI Gui Hardware Java Jupyter JupyterBinder Komponenten Links Linux Markdown Markup Music Numerik OpenSource PKI-X.509-CA Präsentationen Python QBrowser Rants Raspi Revisited Security Software-Test sQLshell TeleGrafana Verschiedenes Video Virtualisierung Windows Upcoming...
Ich musste neulich darüber nachdenken, eine Parallelisierung für einen meiner ANT-Tasks in meinem Static Site Generator einzubauen.
WeiterlesenIch habe mich mit der Idee zu diesem Projekt Monate abgequält - hätte ich gewusst, was die eigentliche Implementierung für Qualen verursachen würde, hätte ich sie wahrscheinlich eingestampft.
WeiterlesenDa ich mich in den letzten Wochen wieder einmal mit Javas Sicherheitsmechanismen und dem Erzeugen von Animationen beschäftigt habe, habe ich den Entschluss gefasst, die bisher mittels JMF AVIs in dWb+ zu erstetzen - nur wodurch?
WeiterlesenManche nennen es Blog, manche Web-Seite - ich schreibe hier hin und wieder über meine Erlebnisse, Rückschläge und Erleuchtungen bei meinen Hobbies.
Wer daran teilhaben und eventuell sogar davon profitieren möchte, muss damit leben, daß ich hin und wieder kleine Ausflüge in Bereiche mache, die nichts mit IT, Administration oder Softwareentwicklung zu tun haben.
Ich wünsche allen Lesern viel Spaß und hin und wieder einen kleinen AHA!-Effekt...
PS: Meine öffentlichen Codeberg-Repositories findet man hier.