commit c6e1c219e49f17e375191d6e24155197d79c25f4
Author: Leszek Koltunski <leszek@distoretedandroid.org>
Date:   Mon Jun 13 13:01:45 2016 +0100

    Save PNG effect almost finished. Supporting App (hopefully!) completely finished.
    
    What remains to be done: put actual saving of the Bitmap in a separate thread, away from the Graphics thread!!

diff --git a/src/main/java/org/distorted/library/DistortedObject.java b/src/main/java/org/distorted/library/DistortedObject.java
index dfee9b4..885b042 100644
--- a/src/main/java/org/distorted/library/DistortedObject.java
+++ b/src/main/java/org/distorted/library/DistortedObject.java
@@ -2528,8 +2528,8 @@ public abstract class DistortedObject
  * @param filename Full path to the file.
  * @return         ID of the effect added, or -1 if we failed to add one.
  */
- public long savePNG(String filename)
+ public long savePNG(String filename, int left, int top, int width, int height)
    {
-   return mO.add(EffectNames.SAVE_PNG, filename);
+   return mO.add(EffectNames.SAVE_PNG, filename, left, top, width, height);
    }
 }
diff --git a/src/main/java/org/distorted/library/EffectListener.java b/src/main/java/org/distorted/library/EffectListener.java
index 17c084e..4f529cf 100644
--- a/src/main/java/org/distorted/library/EffectListener.java
+++ b/src/main/java/org/distorted/library/EffectListener.java
@@ -9,24 +9,23 @@ package org.distorted.library;
 
 public interface EffectListener 
   {
-
 /**
  * Gets called when event of type 'em' happens to effect 'effectID'. 
  * 
- * @param eventMessage Type of event that happened.
- * @param effectID     ID of the effect the event happened to. This ID must have been previously returned by one
- *                     of the DistortedBitmap.{deform,distort,move,...} functions.
- * @param effectType   Type of the effect as defined in EffectNames, e.g. if effectType==EffectNames.MOVE.ordinal(),
- *                     then the event happened to a MOVE effect. 
- * @param bitmapID     the ID of the DistortedBitmap object, as returned by {@link DistortedBitmap#getID()}, this event 
- *                     happened to. If the object has been created using a copy constructor from another instance of
- *                     DistortedBitmap, the ID here will be the one of the original object.     
- *                                     
+ * @param eventType  Type of event that happened.
+ * @param effectID   ID of the effect the event happened to. This ID must have been previously returned by one
+ *                   of the DistortedBitmap.{deform,distort,move,...} functions.
+ * @param effectName Name of the effect as defined in EffectNames, e.g. if effectType==EffectNames.MOVE.ordinal(),
+ *                   then the event happened to a MOVE effect.
+ * @param bitmapID   the ID of the DistortedBitmap object, as returned by {@link DistortedBitmap#getID()}, this event
+ *                   happened to. If the object has been created using a copy constructor from another instance of
+ *                   DistortedBitmap, the ID here will be the one of the original object.
+ * @param message    Any message string associated with it. 'Failed' event types have one.
  * @see EffectMessage
  * @see EffectNames
  */
    
-  void effectMessage(EffectMessage eventMessage, long effectID, int effectType, long bitmapID);
+  void effectMessage(final EffectMessage eventType, final long effectID, final int effectName, final long bitmapID, final String message);
   }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/EffectMessage.java b/src/main/java/org/distorted/library/EffectMessage.java
index 1de0ea9..11ddf49 100644
--- a/src/main/java/org/distorted/library/EffectMessage.java
+++ b/src/main/java/org/distorted/library/EffectMessage.java
@@ -11,7 +11,7 @@ public enum EffectMessage
  * The effect has been removed. This can happen if:
  * <ul>
  * <li> someone explicitly removed the effect with a call to {@link DistortedBitmap#abortEffect(long)} (or one of the other 'abort' methods)
- * <li> the interpolation of the effect has finished and the end result is equal to the effect's zero point.
+ * <li> the interpolation of the effect has finished and the end result is equal to the effect's unity.
  * </ul>    
  */
   EFFECT_REMOVED,
@@ -25,7 +25,7 @@ public enum EffectMessage
  * finishes. You will never get this message if you set the effect to go on indefinitely with a call to 
  * {@link Interpolator#setCount(float)}.
  * <p>  
- * If then the end effect is equal to the effect's zero point, then immediately after this message you 
+ * If then the end effect is equal to the effect's unity, then immediately after this message you
  * will also get a EFFECT_REMOVED message.
  */
   EFFECT_FINISHED,
@@ -35,7 +35,7 @@ public enum EffectMessage
  * <p>
  * Currently only OTHER effects (saving to PNG file and to a MP4 movie) can fail.
  */
-  EFFECT_FAILED;
+  EFFECT_FAILED
   }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/EffectMessageSender.java b/src/main/java/org/distorted/library/EffectMessageSender.java
index ab8c62a..013ee79 100644
--- a/src/main/java/org/distorted/library/EffectMessageSender.java
+++ b/src/main/java/org/distorted/library/EffectMessageSender.java
@@ -11,16 +11,18 @@ final class EffectMessageSender extends Thread
     EffectListener mListener;
     EffectMessage mMessage;
     long mEffectID;
-    int mEffectType;
+    int mEffectName;
     long mBitmapID;
-   
-    Message(EffectListener l, EffectMessage m, long id, int type, long bmpID)
+    String mStr;
+
+    Message(EffectListener l, EffectMessage m, long id, int name, long bmpID, String str)
       {
       mListener   = l;
       mMessage    = m;
       mEffectID   = id;
-      mEffectType = type;
+      mEffectName = name;
       mBitmapID   = bmpID;
+      mStr        = str;
       }
     }
   
@@ -42,7 +44,7 @@ final class EffectMessageSender extends Thread
        
     if( mThis==null )
       {
-      mList = new Vector<Message>();
+      mList = new Vector<>();
       mThis = new EffectMessageSender();
       mThis.start();
       }
@@ -73,7 +75,7 @@ final class EffectMessageSender extends Thread
       if( mList.size()>0 )
         {
         tmp = mList.get(0); 
-        tmp.mListener.effectMessage(tmp.mMessage, tmp.mEffectID, tmp.mEffectType, tmp.mBitmapID);
+        tmp.mListener.effectMessage(tmp.mMessage, tmp.mEffectID, tmp.mEffectName, tmp.mBitmapID, tmp.mStr);
         mList.remove(0);
         }
      
@@ -90,9 +92,9 @@ final class EffectMessageSender extends Thread
   
 ///////////////////////////////////////////////////////////////////////////////////////////////////
         
-  static void newMessage(EffectListener l, EffectMessage m, long id, int type, long bmpID)
+  static void newMessage(EffectListener l, EffectMessage m, long id, int name, long bmpID, String str)
     {
-    Message msg = mThis.new Message(l,m,id,type,bmpID);  
+    Message msg = mThis.new Message(l,m,id,name,bmpID,str);
     mList.add(msg);   
     }
   }
diff --git a/src/main/java/org/distorted/library/EffectNames.java b/src/main/java/org/distorted/library/EffectNames.java
index 19d6012..3e00247 100644
--- a/src/main/java/org/distorted/library/EffectNames.java
+++ b/src/main/java/org/distorted/library/EffectNames.java
@@ -90,7 +90,8 @@ enum EffectNames
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-  
+// yes, I know we could have used values(i)
+
   static EffectTypes getType(int ordinal)
     {
     if( ordinal<DISTORT.ordinal()     ) return EffectTypes.MATRIX;
diff --git a/src/main/java/org/distorted/library/EffectQueue.java b/src/main/java/org/distorted/library/EffectQueue.java
index e994a94..4b34561 100644
--- a/src/main/java/org/distorted/library/EffectQueue.java
+++ b/src/main/java/org/distorted/library/EffectQueue.java
@@ -157,6 +157,7 @@ abstract class EffectQueue
       if( mType[i]==ord )
         {
         remove(i);
+        // TODO: don't we have to do a 'i--' here? Check this.
         ret = true;
         }
       }
@@ -227,7 +228,8 @@ abstract class EffectQueue
                                       EffectMessage.EFFECT_REMOVED, 
                                       (removedID<<EffectTypes.LENGTH)+EffectNames.getType(removedType).type,
                                       removedType,
-                                      mBitmapID);  
+                                      mBitmapID,
+                                      null);
     }
   
 ///////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/main/java/org/distorted/library/EffectQueueFragment.java b/src/main/java/org/distorted/library/EffectQueueFragment.java
index 20637d6..8c6cd6c 100644
--- a/src/main/java/org/distorted/library/EffectQueueFragment.java
+++ b/src/main/java/org/distorted/library/EffectQueueFragment.java
@@ -55,7 +55,8 @@ class EffectQueueFragment extends EffectQueue
                                           EffectMessage.EFFECT_FINISHED, 
                                           (mID[i]<<EffectTypes.LENGTH)+EffectTypes.FRAGMENT.type,
                                           mType[i], 
-                                          mBitmapID); 
+                                          mBitmapID,
+                                          null);
       
         if( EffectNames.isUnity(mType[i], mUniforms, NUM_UNIFORMS*i) )
           {
diff --git a/src/main/java/org/distorted/library/EffectQueueMatrix.java b/src/main/java/org/distorted/library/EffectQueueMatrix.java
index eac17f1..ca7b8b3 100644
--- a/src/main/java/org/distorted/library/EffectQueueMatrix.java
+++ b/src/main/java/org/distorted/library/EffectQueueMatrix.java
@@ -88,7 +88,8 @@ class EffectQueueMatrix extends EffectQueue
                                           EffectMessage.EFFECT_FINISHED, 
                                          (mID[i]<<EffectTypes.LENGTH)+EffectTypes.MATRIX.type,
                                           mType[i], 
-                                          mBitmapID); 
+                                          mBitmapID,
+                                          null);
        
         if( EffectNames.isUnity(mType[i], mUniforms, NUM_UNIFORMS*i+3) )
           {  
diff --git a/src/main/java/org/distorted/library/EffectQueueOther.java b/src/main/java/org/distorted/library/EffectQueueOther.java
index bc60f9c..7089b44 100644
--- a/src/main/java/org/distorted/library/EffectQueueOther.java
+++ b/src/main/java/org/distorted/library/EffectQueueOther.java
@@ -13,7 +13,7 @@ import java.nio.ByteOrder;
 
 class EffectQueueOther extends EffectQueue
   {
-  private static final int NUM_UNIFORMS = 0;
+  private static final int NUM_UNIFORMS = 4;
   private static final int INDEX = EffectTypes.OTHER.ordinal();
   private String[] mFilename;
 
@@ -34,6 +34,11 @@ class EffectQueueOther extends EffectQueue
   protected void moveEffect(int index)
     {
     mFilename[index] = mFilename[index+1];
+
+    mUniforms[NUM_UNIFORMS*index  ] = mUniforms[NUM_UNIFORMS*(index+1)  ];
+    mUniforms[NUM_UNIFORMS*index+1] = mUniforms[NUM_UNIFORMS*(index+1)+1];
+    mUniforms[NUM_UNIFORMS*index+2] = mUniforms[NUM_UNIFORMS*(index+1)+2];
+    mUniforms[NUM_UNIFORMS*index+3] = mUniforms[NUM_UNIFORMS*(index+1)+3];
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -45,18 +50,26 @@ class EffectQueueOther extends EffectQueue
       {
       if (mType[i] == EffectNames.SAVE_PNG.ordinal() )
         {
-        ByteBuffer buf = ByteBuffer.allocateDirect( (int)(mObjHalfX * mObjHalfY * 16) );
+        int left  = (int)mUniforms[NUM_UNIFORMS*i  ];
+        int top   = (int)mUniforms[NUM_UNIFORMS*i+1];
+        int width = (int)mUniforms[NUM_UNIFORMS*i+2];
+        int height= (int)mUniforms[NUM_UNIFORMS*i+3];
+
+        ByteBuffer buf = ByteBuffer.allocateDirect( width*height*4 );
         buf.order(ByteOrder.LITTLE_ENDIAN);
-        GLES20.glReadPixels(0, 0, (int)(2*mObjHalfX), (int)(2*mObjHalfY) , GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
+        GLES20.glReadPixels( left, top, width, height , GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
 
+        flipUpsideDown(buf,width,height); // GL uses a coordinate system from mathematics; i.e.
+                                          // (0,0) is in the lower-left corner. 2D stuff has
+                                          // the origin on the upper-left corner; we have to flip
+                                          // out bitmap upside down!
         buf.rewind();
-
         BufferedOutputStream bos = null;
 
         try
           {
           bos = new BufferedOutputStream(new FileOutputStream(mFilename[i]));
-          Bitmap bmp = Bitmap.createBitmap( (int)(2*mObjHalfX), (int)(2*mObjHalfY), Bitmap.Config.ARGB_8888);
+          Bitmap bmp = Bitmap.createBitmap( width, height, Bitmap.Config.ARGB_8888);
           bmp.copyPixelsFromBuffer(buf);
           bmp.compress(Bitmap.CompressFormat.PNG, 90, bos);
           bmp.recycle();
@@ -66,7 +79,8 @@ class EffectQueueOther extends EffectQueue
                                             EffectMessage.EFFECT_FINISHED,
                                            (mID[i]<<EffectTypes.LENGTH)+EffectTypes.OTHER.type,
                                             mType[i],
-                                            mBitmapID);
+                                            mBitmapID,
+                                            null);
           }
         catch(Exception e)
           {
@@ -75,7 +89,8 @@ class EffectQueueOther extends EffectQueue
                                             EffectMessage.EFFECT_FAILED,
                                            (mID[i]<<EffectTypes.LENGTH)+EffectTypes.OTHER.type,
                                             mType[i],
-                                            mBitmapID);
+                                            mBitmapID,
+                                            e.getMessage());
           }
         finally
           {
@@ -99,7 +114,7 @@ class EffectQueueOther extends EffectQueue
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  synchronized long add(EffectNames eln, String filename)
+  synchronized long add(EffectNames eln, String filename, int left, int top, int width, int height)
     {
     if( mMax[INDEX]>mNumEffects )
       {
@@ -107,9 +122,35 @@ class EffectQueueOther extends EffectQueue
       mInterI[mNumEffects] = null;
       mInterP[mNumEffects] = null;
 
+      mUniforms[NUM_UNIFORMS*mNumEffects  ] =  left;
+      mUniforms[NUM_UNIFORMS*mNumEffects+1] =   top;
+      mUniforms[NUM_UNIFORMS*mNumEffects+2] = width;
+      mUniforms[NUM_UNIFORMS*mNumEffects+3] =height;
+
       return addBase(eln);
       }
 
     return -1;
     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void flipUpsideDown(ByteBuffer buf, int width, int height)
+    {
+    byte[] tmp1 = new byte[width*4];
+    byte[] tmp2 = new byte[width*4];
+
+    for(int i=0; i<height/2; i++)
+      {
+      buf.position((         i)*width*4);
+      buf.get(tmp1);
+      buf.position((height-1-i)*width*4);
+      buf.get(tmp2);
+
+      buf.position((         i)*width*4);
+      buf.put(tmp2);
+      buf.position((height-1-i)*width*4);
+      buf.put(tmp1);
+      }
+    }
   }
diff --git a/src/main/java/org/distorted/library/EffectQueueVertex.java b/src/main/java/org/distorted/library/EffectQueueVertex.java
index f0d40c4..ba9e373 100644
--- a/src/main/java/org/distorted/library/EffectQueueVertex.java
+++ b/src/main/java/org/distorted/library/EffectQueueVertex.java
@@ -55,7 +55,8 @@ class EffectQueueVertex extends EffectQueue
                                           EffectMessage.EFFECT_FINISHED, 
                                          (mID[i]<<EffectTypes.LENGTH)+EffectTypes.VERTEX.type,
                                           mType[i], 
-                                          mBitmapID); 
+                                          mBitmapID,
+                                          null);
       
         if( EffectNames.isUnity(mType[i], mUniforms, NUM_UNIFORMS*i) )
           {
diff --git a/src/main/java/org/distorted/library/EffectTypes.java b/src/main/java/org/distorted/library/EffectTypes.java
index 9577abb..5d89773 100644
--- a/src/main/java/org/distorted/library/EffectTypes.java
+++ b/src/main/java/org/distorted/library/EffectTypes.java
@@ -10,21 +10,20 @@ package org.distorted.library;
 public enum EffectTypes
   {
   /**
-   * Matrix effects - i.e. effect that change the ModelView matrix. Rotations, Moves, Shears, Scales.
+   * Effects that change the ModelView matrix: Rotations, Moves, Shears, Scales.
    */
   MATRIX   ( 0x1 ),   // we will need to perform bitwise operations on those - so keep the values 1,2,4,8...
   /**
-   * Vertex Effects - i.e. those that get executed in the Vertex shader. Generally various distortions
-   * of the vertices.
+   * Effects that get executed in the Vertex shader: various distortions of the vertices.
    */
   VERTEX   ( 0x2 ),
   /**
-   * Fragment Effects - executed in the Fragment shader. Changes of color, hue, transparency levels, etc.
+   * Effects executed in the Fragment shader: changes of color, hue, transparency levels, etc.
    */
   FRAGMENT ( 0x4 ),
   /**
-   * effects that did not belong to anywhere else - currently saving the contents of underlying Surface
-   * to a PNG or a MP4 file.
+   * Effects that did not belong to anywhere else - currently only saving the contents of underlying
+   * Surface to a PNG or a MP4 file.
    */
   OTHER    ( 0x8 );
 
@@ -54,5 +53,4 @@ public enum EffectTypes
     maxtable[3] = 5;  // Max 5 OTHER Effects
     }
 ///////////////////////////////////////////////////////////////////////////////////////////////////
-  }
-
+  }
\ No newline at end of file
