1. package test;
  2.  
  3. /*
  4.  * A simple example of how to invert/modify bitmaps with java.
  5.  *
  6.  * Copyright (C) 2012  Ma_Sys.ma
  7.  *
  8.  * This program is free software: you can redistribute it and/or modify
  9.  * it under the terms of the GNU General Public License as published by
  10.  * the Free Software Foundation, either version 3 of the License, or
  11.  * (at your option) any later version.
  12.  *
  13.  * This program is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.  * GNU General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU General Public License
  19.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  20.  */
  21.  
  22. // Basic Imports that could be helpful
  23. import javax.imageio.*;
  24. import java.awt.*;
  25. import java.awt.image.*;
  26. import java.io.*;
  27. import java.lang.*;
  28. import java.lang.reflect.*;
  29. import java.util.*;
  30.  
  31. public class InvertColors {
  32.  
  33.     public static class LoopInverter extends DistributedLoopThread {
  34.  
  35.         private BufferedImage img;
  36.    
  37.         public LoopInverter(Integer start, Integer end, DistributedLoop ownerLoop) {
  38.             super(start, end, ownerLoop);
  39.         }
  40.        
  41.         public void pass(Object...data) {
  42.             img = (BufferedImage)data[0];
  43.         }
  44.  
  45.         protected void execLoop() {
  46.             int width = img.getWidth();
  47.             int ems = END - START; // end minus start
  48.             // Get an area of pixels
  49.             int[] pixels = img.getRGB(0, START, img.getWidth(), ems, null, 0, width);
  50.             for(int j = 0; j < pixels.length; j++) {
  51.                 // Actual inversion
  52.                 pixels[j] = 0xffffff ^ pixels[j];
  53.             }
  54.             // Store the pixels back into the image
  55.             img.setRGB(0, START, width, ems, pixels, 0, width);
  56.         }
  57.  
  58.     }
  59.  
  60.     public static void main(String[] args) {
  61.         System.out.println("Invert Colors 1.0, Copyright (c) 2012 Ma_Sys.ma.");
  62.         System.out.println("For further info send an e-mail to Ma_Sys.ma@web.de.");
  63.         System.out.println();
  64.         if(args.length != 1) {
  65.             System.out.println("Usage: java test.InvertColors [image]");
  66.             System.exit(1);
  67.         }
  68.  
  69.         int extensionPosition = args[0].lastIndexOf('.');
  70.         if(extensionPosition == -1) {
  71.             System.out.println("Unable to determine input filetype.");
  72.             System.exit(1);
  73.         }
  74.  
  75.         // Read image data
  76.         BufferedImage img;
  77.         try {
  78.             img = ImageIO.read(new File(args[0]));
  79.         } catch(IOException ex) {
  80.             System.err.println("Unable to read image from " + args[0]);
  81.             ex.printStackTrace();
  82.             System.exit(2);
  83.             return; // "Variable might not have been initialized."
  84.         }
  85.  
  86.         long start = System.currentTimeMillis(); // Measure time
  87.  
  88.         // Invert all pixels using all processors
  89.         // See LoopInverter for the actual calculation
  90.         DistributedLoop loop;
  91.         try {
  92.             loop = new DistributedLoop(DistributedLoop.AUTO_DETERMINE_THREADS, 0, img.getHeight(), LoopInverter.class, null, null);
  93.         } catch(InstantiationException ex) {
  94.             System.err.println("Unable to create distributed loop.");
  95.             ex.printStackTrace();
  96.             System.exit(4);
  97.             return; // s. o.
  98.         }
  99.         loop.passArgumentsOnAll(img);
  100.         loop.startAll();
  101.  
  102.         System.out.println("Took " + (System.currentTimeMillis() - start) + " ms for the actual calculation.");
  103.  
  104.         // Write image data
  105.         try {
  106.             ImageIO.write(img, "png", new File(args[0].substring(0, extensionPosition) + "_inverted.png"));
  107.         } catch(IOException ex) {
  108.             System.err.println("Unable to write PNG image file.");
  109.             ex.printStackTrace();
  110.             System.exit(3);
  111.         }
  112.     }
  113.  
  114. }
  115.  
  116. // ---------------------------------------
  117. //  From the Tools Library in Version 2.0
  118. // ---------------------------------------
  119.  
  120. /**
  121.  * <p>
  122.  * Ermöglicht es, Rechenlast auf mehrere Kerne zu verteilen, indem eine
  123.  * Schleife in mehrere Teile aufgesplittet wird. Somit lassen sich Schleifen
  124.  * mit mehr Einträgen als Kerne vorhanden sind problemlos parallelisieren, wenn
  125.  * innerhalb der Schleife kein Ergebnis des vorherigen Durchganges benötigt wird.
  126.  * </p><p>
  127.  * Die Nutzung ist alles andere als einfach und soll deshalb am Beispiel gezeigt
  128.  * werden.
  129.  * </p><p>
  130.  * Alle Werte eines Arrays sollen initialisiert werden, indem
  131.  * komplexe Objekte erzeugt werden, deren Erstellung lange dauert:
  132.  * <br />
  133.  * <pre>
  134.     package ma.tools.concurrent;
  135.    
  136.     import ma.tools.concurrent.DistributedLoop;
  137.     import ma.tools.concurrent.DistributedLoopThread;
  138.    
  139.     public class DistributedLoopExample {
  140.    
  141.         private static int nr = 1;
  142.    
  143.         public DistributedLoopExample() {
  144.             super();
  145.             int objects = 32;
  146.             long start;
  147.            
  148.             // 1. Test mit normaler Schleife
  149.             start = System.currentTimeMillis();
  150.             ComplexObject[] allObjects = new ComplexObject[objects];
  151.             for(int i = 0; i < objects; i++) {
  152.                 allObjects[i] = new ComplexObject();
  153.             }
  154.             System.out.println("Normalerweise braucht man " + (System.currentTimeMillis() - start) + " ms zum Erstellen der Objekte.");
  155.            
  156.             // 2. Test mit verteilter Schleife
  157.             start = System.currentTimeMillis();
  158.             allObjects = new ComplexObject[objects];
  159.             DistributedLoop loop;
  160.             try {
  161.                 loop = new DistributedLoop(DistributedLoop.AUTO_DETERMINE_THREADS, 0, objects, CreatorInstance.class, null, this);
  162.             } catch(InstantiationException ex) {
  163.                 ex.printStackTrace();
  164.                 return;
  165.             }
  166.             // Wir wollen das Array nicht als mehrere Argumente übergeben
  167.             loop.passArgumentsOnAll((Object)allObjects);
  168.             loop.startAll();
  169.             System.out.println("Verteilt dauert es (nur?) " + (System.currentTimeMillis() - start) + " ms");
  170.         }
  171.        
  172.         public class CreatorInstance extends DistributedLoopThread {
  173.  
  174.             private ComplexObject[] allObjects;
  175.            
  176.             public CreatorInstance(Integer start, Integer end, DistributedLoop ownerLoop) {
  177.                 super(start, end, ownerLoop);
  178.             }
  179.            
  180.             protected void pass(Object...data) {
  181.                 allObjects = (ComplexObject[])data[0];
  182.             }
  183.  
  184.             protected void execLoop() {
  185.                 for(int i = START; i &lt; END; i++) {
  186.                     allObjects[i] = new ComplexObject();
  187.                 }
  188.             }
  189.            
  190.         }
  191.        
  192.         private class ComplexObject {
  193.        
  194.             public ComplexObject() {
  195.                 int cnr = nr++;
  196.                 System.out.println("Komplexes Objekt " + cnr + " erstellen...");
  197.                 // Zeit die es braucht um ein Objekt zu erstellen (hier künstlich verlangsamt)
  198.                 try {
  199.                     Thread.sleep(250);
  200.                 } catch(InterruptedException ex) {
  201.                     System.out.println("Komplexes Objekt " + cnr + ": Erstellung abgebrochen: " + ex.toString());
  202.                 }
  203.                 System.out.println("Komplexes Objekt " + cnr + " fertig.");
  204.             }
  205.         }
  206.    
  207.         public static void main(String[] args) {
  208.             new DistributedLoopExample();
  209.         }
  210.    
  211.     }
  212.  * </pre>
  213.  * <br />
  214.  * Wenn man die "künstlich verlängernden" Zeilen wegnimmt, sieht man
  215.  * sehr gut, dass es einen Overhead (auf dem Testsystem rund 3ms) durch
  216.  * die Paralelisierung gibt. Deshalb lohnt es sich nur bei der Erstellung
  217.  * von Objekten, die zusammen "relativ lange" brauchen, was auch schon bei
  218.  * z.B. 200 ms der Fall ist. Dadurch lohnt sich das Verteilte Rechnen an
  219.  * vielen Stellen.
  220.  * </p>
  221.  * <p>
  222.  * Hinweis #1: Bei Spiecherintensiven Anwendungen könnte es anders sein.
  223.  * Hier hilft es, beides zu testen.
  224.  * Das ist sehr einfach möglich, indem man dem Constructor als
  225.  * "preferredThreadCount" die Zahl 1 (keine parallelisierung)
  226.  * übergibt. Dies vermindert den Overhead aber nur sehr geringfügig.
  227.  * Wenn es knapp ist, sollte man eine "richtige" Schleife testen.
  228.  * </p><p>
  229.  * Hinweis #2: Rechenintensive Anwendungen profitieren eventuell vom
  230.  * Java Native Interface.
  231.  * </p><p>
  232.  * Hinweis #3: Optimieren Sie auch den Inhalt der Schleifen
  233.  * Sollte eigentlich klar sein.
  234.  * </p>
  235.  *
  236.  * @version 1.0.1
  237.  * @author Linux-Fan, Ma_Sys.ma
  238.  */
  239. class DistributedLoop {
  240.  
  241.     public static final int AUTO_DETERMINE_THREADS = -1;
  242.    
  243.     private DistributedLoopThread[] allThreads;
  244.    
  245.     private int readyThreads;
  246.    
  247.     private boolean started;
  248.    
  249.     private DistributedLoopUser owner;
  250.    
  251.     private ArrayList<Exception> theExceptions;
  252.    
  253.     public DistributedLoop(int preferredThreadCount, int lStart, int lEnd, Class<? extends DistributedLoopThread> threadClass, DistributedLoopUser owner, Object enclosing) throws InstantiationException {
  254.         super();
  255.         this.owner = owner;
  256.         theExceptions = new ArrayList<Exception>();
  257.         int realThreadCount = 0;
  258.         if(preferredThreadCount == AUTO_DETERMINE_THREADS) {
  259.             realThreadCount = Runtime.getRuntime().availableProcessors();
  260.         } else {
  261.             realThreadCount = preferredThreadCount;
  262.         }
  263.         int range = lEnd - lStart;
  264.         if(range < realThreadCount) {
  265.             realThreadCount = range;
  266.         }
  267.         allThreads = new DistributedLoopThread[realThreadCount];
  268.         readyThreads = 0;
  269.         started = false;
  270.         int perThread = range / allThreads.length;
  271.         for(int i = 0; i < allThreads.length; i++) {
  272.             final int cStart = lStart + perThread * i;
  273.             final int cEnd;
  274.             if(i == (allThreads.length - 1)) {
  275.                 cEnd = lEnd;
  276.             } else {
  277.                 cEnd = lStart + perThread * (i + 1);
  278.             }
  279.             try {
  280.                 if(enclosing == null) {
  281.                     allThreads[i] = threadClass.getConstructor(Integer.class, Integer.class, getClass()).newInstance(cStart, cEnd, this);
  282.                 } else {
  283.                     allThreads[i] = threadClass.getConstructor(enclosing.getClass(), Integer.class, Integer.class, getClass()).newInstance(enclosing, cStart, cEnd, this);
  284.                 }
  285.                 allThreads[i].setName("Distributed Loop Thread " + (i + 1));
  286.             } catch(Exception ex) {
  287.                 InstantiationException er = new InstantiationException("Unable to create distributed loop thread.");
  288.                 er.initCause(ex);
  289.                 throw er;
  290.             }
  291.         }
  292.     }
  293.    
  294.     public Iterator<Exception> startAll() {
  295.         if(!started) {
  296.             started = true;
  297.             for(int i = 0; i < allThreads.length; i++) {
  298.                 allThreads[i].start();
  299.             }
  300.             if(owner == null) {
  301.                 for(int i = 0; i < allThreads.length; i++) {
  302.                     try {
  303.                         allThreads[i].join();
  304.                     } catch(InterruptedException ex) {
  305.                         System.err.println("ma.tools.concurrent.DistributedLoop: Thread interrupted: " + ex.toString());
  306.                     } catch(IllegalThreadStateException ex) {
  307.                         System.err.println("ma.tools.concurrent.DistributedLoop: " + ex.toString());
  308.                     }
  309.                 }
  310.                 return theExceptions.iterator();
  311.             } else {
  312.                 return null;
  313.             }
  314.         } else {
  315.             throw new IllegalThreadStateException("Distributed loop was already started.");
  316.         }
  317.     }
  318.    
  319.     public void interruptAll() {
  320.         for(int i = 0; i < allThreads.length; i++) {
  321.             if(allThreads[i].isAlive()) {
  322.                 allThreads[i].interrupt();
  323.             }
  324.         }
  325.     }
  326.    
  327.     public Object[] callOnAll(Method m, Object...params) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
  328.         Object[] allRets = new Object[allThreads.length];
  329.         for(int i = 0; i < allThreads.length; i++) {
  330.             allRets[i] = m.invoke(allThreads[i], params);
  331.         }
  332.         return allRets;
  333.     }
  334.    
  335.     public DistributedLoopThread[] getThreads() {
  336.         return allThreads;
  337.     }
  338.    
  339.     public void passArgumentsOnAll(Object...data) {
  340.         for(int i = 0; i < allThreads.length; i++) {
  341.             allThreads[i].pass(data);
  342.         }
  343.     }
  344.    
  345.     protected void readyCountUp() {
  346.         readyThreads++;
  347.         if(readyThreads == allThreads.length && owner != null) {
  348.             owner.distributedThreadsReady(theExceptions.toArray(new Exception[theExceptions.size()]));
  349.         }
  350.     }
  351.    
  352.     protected void passException(Exception ex) {
  353.         theExceptions.add(ex);
  354.     }
  355.    
  356.     public Iterator<Exception> getExceptions() {
  357.         return theExceptions.iterator();
  358.     }
  359.    
  360. }
  361.  
  362. abstract class DistributedLoopThread extends Thread {
  363.  
  364.     protected final int START;
  365.     protected final int END;
  366.     protected final DistributedLoop OWNER;
  367.    
  368.     public DistributedLoopThread(Integer start, Integer end, DistributedLoop ownerLoop) {
  369.         super();
  370.         this.START = start;
  371.         this.END   = end;
  372.         this.OWNER = ownerLoop;
  373.     }
  374.    
  375.     public void run() {
  376.         try {
  377.             execLoop();
  378.         } catch(Exception ex) {
  379.             OWNER.passException(ex);
  380.         }
  381.         OWNER.readyCountUp();
  382.     }
  383.    
  384.     protected void pass(@SuppressWarnings("unused") Object...data) {}
  385.    
  386.     protected abstract void execLoop() throws Exception;
  387.    
  388. }
  389.  
  390. /**
  391.  * <p>Veraltetes Interface.</p>
  392.  * <p>
  393.  * Kann genutzt werden, wenn man beim beenden alle Threads in
  394.  * eine andere Methode springen will.
  395.  * </p>
  396.  *
  397.  * @version 1.0.0.1
  398.  * @author Linux-Fan, Ma_Sys.ma
  399.  * @deprecated
  400.  *      Mittlerweile ist es möglich, dass {@link DistributedLoop#startAll()}
  401.  *      auf das Beenden der Threads wartet, ohne einen eigenen Wartethread zu
  402.  *      erzeugen.
  403.  */
  404. interface DistributedLoopUser {
  405.  
  406.     public void distributedThreadsReady(Exception[] passed);
  407.    
  408. }