package test;
/*
* A simple example of how to invert/modify bitmaps with java.
*
* Copyright (C) 2012 Ma_Sys.ma
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Basic Imports that could be helpful
import javax.imageio.*;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.lang.*;
import java.lang.reflect.*;
import java.util.*;
public class InvertColors {
public static class LoopInverter extends DistributedLoopThread {
private BufferedImage img;
public LoopInverter(Integer start, Integer end, DistributedLoop ownerLoop) {
super(start, end, ownerLoop);
}
public void pass(Object...data) {
img = (BufferedImage)data[0];
}
protected void execLoop() {
int width = img.getWidth();
int ems = END - START; // end minus start
// Get an area of pixels
int[] pixels = img.getRGB(0, START, img.getWidth(), ems, null, 0, width);
for(int j = 0; j < pixels.length; j++) {
// Actual inversion
pixels[j] = 0xffffff ^ pixels[j];
}
// Store the pixels back into the image
img.setRGB(0, START, width, ems, pixels, 0, width);
}
}
public static void main(String[] args) {
System.out.println("Invert Colors 1.0, Copyright (c) 2012 Ma_Sys.ma.");
System.out.println("For further info send an e-mail to Ma_Sys.ma@web.de.");
System.out.println();
if(args.length != 1) {
System.out.println("Usage: java test.InvertColors [image]");
System.exit(1);
}
int extensionPosition = args[0].lastIndexOf('.');
if(extensionPosition == -1) {
System.out.println("Unable to determine input filetype.");
System.exit(1);
}
// Read image data
BufferedImage img;
try {
img = ImageIO.read(new File(args[0]));
} catch(IOException ex) {
System.err.println("Unable to read image from " + args[0]);
ex.printStackTrace();
System.exit(2);
return; // "Variable might not have been initialized."
}
long start = System.currentTimeMillis(); // Measure time
// Invert all pixels using all processors
// See LoopInverter for the actual calculation
DistributedLoop loop;
try {
loop = new DistributedLoop(DistributedLoop.AUTO_DETERMINE_THREADS, 0, img.getHeight(), LoopInverter.class, null, null);
} catch(InstantiationException ex) {
System.err.println("Unable to create distributed loop.");
ex.printStackTrace();
System.exit(4);
return; // s. o.
}
loop.passArgumentsOnAll(img);
loop.startAll();
System.out.println("Took " + (System.currentTimeMillis() - start) + " ms for the actual calculation.");
// Write image data
try {
ImageIO.write(img, "png", new File(args[0].substring(0, extensionPosition) + "_inverted.png"));
} catch(IOException ex) {
System.err.println("Unable to write PNG image file.");
ex.printStackTrace();
System.exit(3);
}
}
}
// ---------------------------------------
// From the Tools Library in Version 2.0
// ---------------------------------------
/**
* <p>
* Ermöglicht es, Rechenlast auf mehrere Kerne zu verteilen, indem eine
* Schleife in mehrere Teile aufgesplittet wird. Somit lassen sich Schleifen
* mit mehr Einträgen als Kerne vorhanden sind problemlos parallelisieren, wenn
* innerhalb der Schleife kein Ergebnis des vorherigen Durchganges benötigt wird.
* </p><p>
* Die Nutzung ist alles andere als einfach und soll deshalb am Beispiel gezeigt
* werden.
* </p><p>
* Alle Werte eines Arrays sollen initialisiert werden, indem
* komplexe Objekte erzeugt werden, deren Erstellung lange dauert:
* <br />
* <pre>
package ma.tools.concurrent;
import ma.tools.concurrent.DistributedLoop;
import ma.tools.concurrent.DistributedLoopThread;
public class DistributedLoopExample {
private static int nr = 1;
public DistributedLoopExample() {
super();
int objects = 32;
long start;
// 1. Test mit normaler Schleife
start = System.currentTimeMillis();
ComplexObject[] allObjects = new ComplexObject[objects];
for(int i = 0; i < objects; i++) {
allObjects[i] = new ComplexObject();
}
System.out.println("Normalerweise braucht man " + (System.currentTimeMillis() - start) + " ms zum Erstellen der Objekte.");
// 2. Test mit verteilter Schleife
start = System.currentTimeMillis();
allObjects = new ComplexObject[objects];
DistributedLoop loop;
try {
loop = new DistributedLoop(DistributedLoop.AUTO_DETERMINE_THREADS, 0, objects, CreatorInstance.class, null, this);
} catch(InstantiationException ex) {
ex.printStackTrace();
return;
}
// Wir wollen das Array nicht als mehrere Argumente übergeben
loop.passArgumentsOnAll((Object)allObjects);
loop.startAll();
System.out.println("Verteilt dauert es (nur?) " + (System.currentTimeMillis() - start) + " ms");
}
public class CreatorInstance extends DistributedLoopThread {
private ComplexObject[] allObjects;
public CreatorInstance(Integer start, Integer end, DistributedLoop ownerLoop) {
super(start, end, ownerLoop);
}
protected void pass(Object...data) {
allObjects = (ComplexObject[])data[0];
}
protected void execLoop() {
for(int i = START; i < END; i++) {
allObjects[i] = new ComplexObject();
}
}
}
private class ComplexObject {
public ComplexObject() {
int cnr = nr++;
System.out.println("Komplexes Objekt " + cnr + " erstellen...");
// Zeit die es braucht um ein Objekt zu erstellen (hier künstlich verlangsamt)
try {
Thread.sleep(250);
} catch(InterruptedException ex) {
System.out.println("Komplexes Objekt " + cnr + ": Erstellung abgebrochen: " + ex.toString());
}
System.out.println("Komplexes Objekt " + cnr + " fertig.");
}
}
public static void main(String[] args) {
new DistributedLoopExample();
}
}
* </pre>
* <br />
* Wenn man die "künstlich verlängernden" Zeilen wegnimmt, sieht man
* sehr gut, dass es einen Overhead (auf dem Testsystem rund 3ms) durch
* die Paralelisierung gibt. Deshalb lohnt es sich nur bei der Erstellung
* von Objekten, die zusammen "relativ lange" brauchen, was auch schon bei
* z.B. 200 ms der Fall ist. Dadurch lohnt sich das Verteilte Rechnen an
* vielen Stellen.
* </p>
* <p>
* Hinweis #1: Bei Spiecherintensiven Anwendungen könnte es anders sein.
* Hier hilft es, beides zu testen.
* Das ist sehr einfach möglich, indem man dem Constructor als
* "preferredThreadCount" die Zahl 1 (keine parallelisierung)
* übergibt. Dies vermindert den Overhead aber nur sehr geringfügig.
* Wenn es knapp ist, sollte man eine "richtige" Schleife testen.
* </p><p>
* Hinweis #2: Rechenintensive Anwendungen profitieren eventuell vom
* Java Native Interface.
* </p><p>
* Hinweis #3: Optimieren Sie auch den Inhalt der Schleifen
* Sollte eigentlich klar sein.
* </p>
*
* @version 1.0.1
* @author Linux-Fan, Ma_Sys.ma
*/
class DistributedLoop {
public static final int AUTO_DETERMINE_THREADS = -1;
private DistributedLoopThread[] allThreads;
private int readyThreads;
private boolean started;
private DistributedLoopUser owner;
private ArrayList<Exception> theExceptions;
public DistributedLoop(int preferredThreadCount, int lStart, int lEnd, Class<? extends DistributedLoopThread> threadClass, DistributedLoopUser owner, Object enclosing) throws InstantiationException {
super();
this.owner = owner;
theExceptions = new ArrayList<Exception>();
int realThreadCount = 0;
if(preferredThreadCount == AUTO_DETERMINE_THREADS) {
realThreadCount = Runtime.getRuntime().availableProcessors();
} else {
realThreadCount = preferredThreadCount;
}
int range = lEnd - lStart;
if(range < realThreadCount) {
realThreadCount = range;
}
allThreads = new DistributedLoopThread[realThreadCount];
readyThreads = 0;
started = false;
int perThread = range / allThreads.length;
for(int i = 0; i < allThreads.length; i++) {
final int cStart = lStart + perThread * i;
final int cEnd;
if(i == (allThreads.length - 1)) {
cEnd = lEnd;
} else {
cEnd = lStart + perThread * (i + 1);
}
try {
if(enclosing == null) {
allThreads[i] = threadClass.getConstructor(Integer.class, Integer.class, getClass()).newInstance(cStart, cEnd, this);
} else {
allThreads[i] = threadClass.getConstructor(enclosing.getClass(), Integer.class, Integer.class, getClass()).newInstance(enclosing, cStart, cEnd, this);
}
allThreads[i].setName("Distributed Loop Thread " + (i + 1));
} catch(Exception ex) {
InstantiationException er = new InstantiationException("Unable to create distributed loop thread.");
er.initCause(ex);
throw er;
}
}
}
public Iterator<Exception> startAll() {
if(!started) {
started = true;
for(int i = 0; i < allThreads.length; i++) {
allThreads[i].start();
}
if(owner == null) {
for(int i = 0; i < allThreads.length; i++) {
try {
allThreads[i].join();
} catch(InterruptedException ex) {
System.err.println("ma.tools.concurrent.DistributedLoop: Thread interrupted: " + ex.toString());
} catch(IllegalThreadStateException ex) {
System.err.println("ma.tools.concurrent.DistributedLoop: " + ex.toString());
}
}
return theExceptions.iterator();
} else {
return null;
}
} else {
throw new IllegalThreadStateException("Distributed loop was already started.");
}
}
public void interruptAll() {
for(int i = 0; i < allThreads.length; i++) {
if(allThreads[i].isAlive()) {
allThreads[i].interrupt();
}
}
}
public Object[] callOnAll(Method m, Object...params) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Object[] allRets = new Object[allThreads.length];
for(int i = 0; i < allThreads.length; i++) {
allRets[i] = m.invoke(allThreads[i], params);
}
return allRets;
}
public DistributedLoopThread[] getThreads() {
return allThreads;
}
public void passArgumentsOnAll(Object...data) {
for(int i = 0; i < allThreads.length; i++) {
allThreads[i].pass(data);
}
}
protected void readyCountUp() {
readyThreads++;
if(readyThreads == allThreads.length && owner != null) {
owner.distributedThreadsReady(theExceptions.toArray(new Exception[theExceptions.size()]));
}
}
protected void passException(Exception ex) {
theExceptions.add(ex);
}
public Iterator<Exception> getExceptions() {
return theExceptions.iterator();
}
}
abstract class DistributedLoopThread extends Thread {
protected final int START;
protected final int END;
protected final DistributedLoop OWNER;
public DistributedLoopThread(Integer start, Integer end, DistributedLoop ownerLoop) {
super();
this.START = start;
this.END = end;
this.OWNER = ownerLoop;
}
public void run() {
try {
execLoop();
} catch(Exception ex) {
OWNER.passException(ex);
}
OWNER.readyCountUp();
}
protected void pass(@SuppressWarnings("unused") Object...data) {}
protected abstract void execLoop() throws Exception;
}
/**
* <p>Veraltetes Interface.</p>
* <p>
* Kann genutzt werden, wenn man beim beenden alle Threads in
* eine andere Methode springen will.
* </p>
*
* @version 1.0.0.1
* @author Linux-Fan, Ma_Sys.ma
* @deprecated
* Mittlerweile ist es möglich, dass {@link DistributedLoop#startAll()}
* auf das Beenden der Threads wartet, ohne einen eigenen Wartethread zu
* erzeugen.
*/
interface DistributedLoopUser {
public void distributedThreadsReady(Exception[] passed);
}