commit 432442f914e969c98b7357cd386f2d3fc0cf72d6
Author: Leszek Koltunski <leszek@distoretedandroid.org>
Date:   Mon Dec 19 14:41:43 2016 +0000

    New DistortedProgram class.

diff --git a/src/main/java/org/distorted/library/Distorted.java b/src/main/java/org/distorted/library/Distorted.java
index 8758b53..b31ed45 100644
--- a/src/main/java/org/distorted/library/Distorted.java
+++ b/src/main/java/org/distorted/library/Distorted.java
@@ -19,16 +19,11 @@
 
 package org.distorted.library;
 
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-
 import android.content.Context;
 import android.opengl.GLES20;
-import android.os.Build;
+import org.distorted.library.program.*;
 
-import org.distorted.library.exception.*;
+import java.io.InputStream;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
@@ -77,9 +72,7 @@ public class Distorted
 
   private static boolean mInitialized = false;
 
-  static int mPositionH;       // model position information
-  static int mNormalH;         // model normal information.
-  static int mTextureCoordH;   // model texture coordinate information.
+  static int[] mAttributes;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -88,186 +81,6 @@ public class Distorted
     
     }
 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  private static void sanitizeMaxValues() throws VertexUniformsException,FragmentUniformsException
-    {
-    int maxV,maxF;  
-    int[] param = new int[1];
-    
-    GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_UNIFORM_VECTORS, param, 0);
-    maxV = param[0];
-    GLES20.glGetIntegerv(GLES20.GL_MAX_FRAGMENT_UNIFORM_VECTORS, param, 0);
-    maxF = param[0];
-    
-    //Log.d("Distorted", "Max vectors in vertex shader: "+maxV);
-    //Log.d("Distorted", "Max vectors in fragment shader: "+maxF);
-    
-    if( !Build.FINGERPRINT.contains("generic") )
-      {
-      int realMaxV = (maxV-11)/4;   // adjust this in case of changes to the shaders...
-      int realMaxF = (maxF- 2)/4;   //
-    
-      if( getMaxVertex()   > realMaxV )
-        {
-        throw new VertexUniformsException("Too many effects in the vertex shader, max is "+realMaxV, realMaxV);
-        }
-      if( getMaxFragment() > realMaxF )
-        {
-        throw new FragmentUniformsException("Too many effects in the fragment shader, max is "+realMaxF, realMaxF);
-        }
-      }
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static int compileShader(final int shaderType, final String shaderSource) throws FragmentCompilationException,VertexCompilationException
-    {
-    int shaderHandle = GLES20.glCreateShader(shaderType);
-
-    if (shaderHandle != 0) 
-      {
-      GLES20.glShaderSource(shaderHandle, "#version 100 \n"+ generateShaderHeader(shaderType) + shaderSource);
-      GLES20.glCompileShader(shaderHandle);
-      final int[] compileStatus = new int[1];
-      GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
-
-      if (compileStatus[0] != GLES20.GL_TRUE ) 
-        {
-        GLES20.glDeleteShader(shaderHandle);
-        shaderHandle = 0;
-        }
-      }
-
-    if (shaderHandle == 0)
-      {     
-      String error = GLES20.glGetShaderInfoLog(shaderHandle);
-     
-      switch(shaderType)
-        {
-        case GLES20.GL_VERTEX_SHADER  : throw new VertexCompilationException(error); 
-        case GLES20.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
-        default                       : throw new RuntimeException(error);
-        }
-      }
-
-    return shaderHandle;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-  private static int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes) throws LinkingException
-    {
-    int programHandle = GLES20.glCreateProgram();
-
-    if (programHandle != 0) 
-      {
-      GLES20.glAttachShader(programHandle, vertexShaderHandle);         
-      GLES20.glAttachShader(programHandle, fragmentShaderHandle);
-
-      if (attributes != null)
-        {
-        final int size = attributes.length;
-
-        for (int i = 0; i < size; i++)
-          {
-          GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
-          }                
-        }
-
-      GLES20.glLinkProgram(programHandle);
-
-      final int[] linkStatus = new int[1];
-      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
-
-      if (linkStatus[0] != GLES20.GL_TRUE ) 
-        {         
-        String error = GLES20.glGetProgramInfoLog(programHandle);
-        GLES20.glDeleteProgram(programHandle);
-        throw new LinkingException(error);
-        }
-      
-      final int[] numberOfUniforms = new int[1];
-      GLES20.glGetProgramiv(programHandle, GLES20.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
-
-      //android.util.Log.d("Distorted", "number of active uniforms="+numberOfUniforms[0]);
-      }
- 
-    return programHandle;
-    }
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  private static String generateShaderHeader(final int type)
-    {
-    String header="";
-   
-    switch(type)
-      {
-      case GLES20.GL_VERTEX_SHADER  : header += ("#define NUM_VERTEX "  + getMaxVertex()+"\n");
-     
-                                      for(EffectNames name: EffectNames.values() )
-                                        {
-                                        if( name.getType()==EffectTypes.VERTEX)
-                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");  
-                                        }
-                                      break;
-      case GLES20.GL_FRAGMENT_SHADER: header += ("#define NUM_FRAGMENT "+ getMaxFragment()+"\n");
-     
-                                      for(EffectNames name: EffectNames.values() )
-                                        {
-                                        if( name.getType()==EffectTypes.FRAGMENT)
-                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");  
-                                        }
-                                      break;
-     }
-   
-    //Log.d(TAG,""+header);
-    
-    return header;
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-  
-  private static String readTextFileFromRawResource(final Context c, final int resourceId)
-    {
-    final InputStream inputStream = c.getResources().openRawResource(resourceId);
-    final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
-    final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
- 
-    String nextLine;
-    final StringBuilder body = new StringBuilder();
- 
-    try
-      {
-      while ((nextLine = bufferedReader.readLine()) != null)
-        {
-        body.append(nextLine);
-        body.append('\n');
-        }
-      }
-    catch (IOException e)
-      {
-      return null;
-      }
- 
-    return body.toString();
-    }
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
- 
-  private static String getVertexShader(final Context c)
-    {
-    return readTextFileFromRawResource( c, R.raw.main_vertex_shader);
-    }
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
- 
-  private static String getFragmentShader(final Context c)
-    {
-    return readTextFileFromRawResource( c, R.raw.main_fragment_shader);
-    }
-
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
   static boolean isInitialized()
@@ -278,9 +91,9 @@ public class Distorted
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 /**
  * When OpenGL context gets created, you need to call this method so that the library can initialise its internal data structures.
- * I.e. best called from GLSurfaceView.onSurfaceCreated().
+ * I.e. best called from GLSurfaceView.onCreate().
  * <p>
- * Compiles the vertex and fragment shaders, establishes the addresses of all uniforms.
+ * Needs to be called from a thread holding the OpenGL context.
  *   
  * @param context Context of the App using the library - used to open up Resources and read Shader code.
  * @throws FragmentCompilationException
@@ -289,26 +102,21 @@ public class Distorted
  * @throws FragmentUniformsException
  * @throws LinkingException
  */
-  public static void onSurfaceCreated(final Context context) throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
+  public static void onCreate(final Context context)
+  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
     { 
     mInitialized = true;  
 
-    final String vertexShader   = Distorted.getVertexShader(context);
-    final String fragmentShader = Distorted.getFragmentShader(context);
-
-    sanitizeMaxValues();
-    
-    final int vertexShaderHandle   = compileShader(GLES20.GL_VERTEX_SHADER  , vertexShader  );     
-    final int fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);     
-      
-    int programH = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, new String[] {"a_Position",  "a_Color", "a_Normal", "a_TexCoordinate"});
+    final InputStream vertexStream   = context.getResources().openRawResource(R.raw.main_vertex_shader);
+    final InputStream fragmentStream = context.getResources().openRawResource(R.raw.main_fragment_shader);
 
+    DistortedProgram mainProgram = new DistortedProgram(vertexStream,fragmentStream);
+    int programH = mainProgram.getProgramHandle();
     GLES20.glUseProgram(programH);
+    mainProgram.bindAndEnableAttributes();
+    mAttributes = mainProgram.getAttributes();
 
     int textureUniformH = GLES20.glGetUniformLocation(programH, "u_Texture");
-    mPositionH          = GLES20.glGetAttribLocation( programH, "a_Position");
-    mNormalH            = GLES20.glGetAttribLocation( programH, "a_Normal");
-    mTextureCoordH      = GLES20.glGetAttribLocation( programH, "a_TexCoordinate");
 
     GLES20.glEnable (GLES20.GL_DEPTH_TEST);
     GLES20.glDepthFunc(GLES20.GL_LEQUAL);
@@ -323,11 +131,7 @@ public class Distorted
     EffectQueueFragment.getUniforms(programH);
     EffectQueueVertex.getUniforms(programH);
     EffectQueueMatrix.getUniforms(programH);
-    
-    GLES20.glEnableVertexAttribArray(mPositionH);        
-    GLES20.glEnableVertexAttribArray(mNormalH);
-    GLES20.glEnableVertexAttribArray(mTextureCoordH);
-   
+
     DistortedTree.reset();
     EffectMessageSender.startSending();
     }
@@ -348,132 +152,4 @@ public class Distorted
    
     mInitialized = false;
     }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the maximum number of Matrix effects.
- *    
- * @return The maximum number of Matrix effects
- */
-  public static int getMaxMatrix()
-    {
-    return EffectQueue.getMax(EffectTypes.MATRIX.ordinal());
-    }
- 
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the maximum number of Vertex effects.
- *    
- * @return The maximum number of Vertex effects
- */  
-  public static int getMaxVertex()
-    {
-    return EffectQueue.getMax(EffectTypes.VERTEX.ordinal());
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the maximum number of Fragment effects.
- *    
- * @return The maximum number of Fragment effects
- */  
-  public static int getMaxFragment()
-    {
-    return EffectQueue.getMax(EffectTypes.FRAGMENT.ordinal());
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Returns the maximum number of Postprocess effects.
- *
- * @return The maximum number of Postprocess effects
- */
-  public static int getMaxPostprocess()
-    {
-    return EffectQueue.getMax(EffectTypes.POSTPROCESS.ordinal());
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets the maximum number of Matrix effects that can be stored in a single EffectQueue at one time.
- * This can fail if:
- * <ul>
- * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
- * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called
- *     before the Vertex Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
- *     time only decreasing the value of 'max' is permitted.
- * <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
- * </ul>
- * 
- * @param max new maximum number of simultaneous Matrix Effects. Has to be a non-negative number not greater
- *            than Byte.MAX_VALUE 
- * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
- */
-  public static boolean setMaxMatrix(int max)
-    {
-    return EffectQueue.setMax(EffectTypes.MATRIX.ordinal(),max);
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets the maximum number of Vertex effects that can be stored in a single EffectQueue at one time.
- * This can fail if:
- * <ul>
- * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
- * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called 
- *     before the Vertex Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
- *     time only decreasing the value of 'max' is permitted.
-* <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
- * </ul>
- * 
- * @param max new maximum number of simultaneous Vertex Effects. Has to be a non-negative number not greater
- *            than Byte.MAX_VALUE 
- * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
- */
-  public static boolean setMaxVertex(int max)
-    {
-    return EffectQueue.setMax(EffectTypes.VERTEX.ordinal(),max);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets the maximum number of Fragment effects that can be stored in a single EffectQueue at one time.
- * This can fail if:
- * <ul>
- * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
- * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called 
- *     before the Fragment Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
- *     time only decreasing the value of 'max' is permitted.
- * <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
- * </ul>
- * 
- * @param max new maximum number of simultaneous Fragment Effects. Has to be a non-negative number not greater
- *            than Byte.MAX_VALUE 
- * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
- */
-  public static boolean setMaxFragment(int max)
-    {
-    return EffectQueue.setMax(EffectTypes.FRAGMENT.ordinal(),max);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Sets the maximum number of Postprocess effects that can be stored in a single EffectQueue at one time.
- * This can fail if:
- * <ul>
- * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
- * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called
- *     before the Fragment Shader gets compiled, i.e. before the call to {@link #onSurfaceCreated}. After this
- *     time only decreasing the value of 'max' is permitted.
- * <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
- * </ul>
- *
- * @param max new maximum number of simultaneous Postprocess Effects. Has to be a non-negative number not greater
- *            than Byte.MAX_VALUE
- * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
- */
-  public static boolean setMaxPostprocess(int max)
-    {
-    return EffectQueue.setMax(EffectTypes.POSTPROCESS.ordinal(),max);
-    }
   }
diff --git a/src/main/java/org/distorted/library/DistortedEffects.java b/src/main/java/org/distorted/library/DistortedEffects.java
index fbdf8c9..a354732 100644
--- a/src/main/java/org/distorted/library/DistortedEffects.java
+++ b/src/main/java/org/distorted/library/DistortedEffects.java
@@ -294,7 +294,135 @@ public class DistortedEffects
 
     return false;
     }
-   
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the maximum number of Matrix effects.
+ *
+ * @return The maximum number of Matrix effects
+ */
+  public static int getMaxMatrix()
+    {
+    return EffectQueue.getMax(EffectTypes.MATRIX.ordinal());
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the maximum number of Vertex effects.
+ *
+ * @return The maximum number of Vertex effects
+ */
+  public static int getMaxVertex()
+    {
+    return EffectQueue.getMax(EffectTypes.VERTEX.ordinal());
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the maximum number of Fragment effects.
+ *
+ * @return The maximum number of Fragment effects
+ */
+  public static int getMaxFragment()
+    {
+    return EffectQueue.getMax(EffectTypes.FRAGMENT.ordinal());
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Returns the maximum number of Postprocess effects.
+ *
+ * @return The maximum number of Postprocess effects
+ */
+  public static int getMaxPostprocess()
+    {
+    return EffectQueue.getMax(EffectTypes.POSTPROCESS.ordinal());
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the maximum number of Matrix effects that can be stored in a single EffectQueue at one time.
+ * This can fail if:
+ * <ul>
+ * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
+ * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called
+ *     before the Vertex Shader gets compiled, i.e. before the call to {@link Distorted#onCreate}. After this
+ *     time only decreasing the value of 'max' is permitted.
+ * <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
+ * </ul>
+ *
+ * @param max new maximum number of simultaneous Matrix Effects. Has to be a non-negative number not greater
+ *            than Byte.MAX_VALUE
+ * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
+ */
+  public static boolean setMaxMatrix(int max)
+    {
+    return EffectQueue.setMax(EffectTypes.MATRIX.ordinal(),max);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the maximum number of Vertex effects that can be stored in a single EffectQueue at one time.
+ * This can fail if:
+ * <ul>
+ * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
+ * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called
+ *     before the Vertex Shader gets compiled, i.e. before the call to {@link Distorted#onCreate}. After this
+ *     time only decreasing the value of 'max' is permitted.
+* <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
+ * </ul>
+ *
+ * @param max new maximum number of simultaneous Vertex Effects. Has to be a non-negative number not greater
+ *            than Byte.MAX_VALUE
+ * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
+ */
+  public static boolean setMaxVertex(int max)
+    {
+    return EffectQueue.setMax(EffectTypes.VERTEX.ordinal(),max);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the maximum number of Fragment effects that can be stored in a single EffectQueue at one time.
+ * This can fail if:
+ * <ul>
+ * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
+ * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called
+ *     before the Fragment Shader gets compiled, i.e. before the call to {@link Distorted#onCreate}. After this
+ *     time only decreasing the value of 'max' is permitted.
+ * <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
+ * </ul>
+ *
+ * @param max new maximum number of simultaneous Fragment Effects. Has to be a non-negative number not greater
+ *            than Byte.MAX_VALUE
+ * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
+ */
+  public static boolean setMaxFragment(int max)
+    {
+    return EffectQueue.setMax(EffectTypes.FRAGMENT.ordinal(),max);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Sets the maximum number of Postprocess effects that can be stored in a single EffectQueue at one time.
+ * This can fail if:
+ * <ul>
+ * <li>the value of 'max' is outside permitted range (0 &le; max &le; Byte.MAX_VALUE)
+ * <li>We try to increase the value of 'max' when it is too late to do so already. It needs to be called
+ *     before the Fragment Shader gets compiled, i.e. before the call to {@link Distorted#onCreate}. After this
+ *     time only decreasing the value of 'max' is permitted.
+ * <li>Furthermore, this needs to be called before any instances of the DistortedEffects class get created.
+ * </ul>
+ *
+ * @param max new maximum number of simultaneous Postprocess Effects. Has to be a non-negative number not greater
+ *            than Byte.MAX_VALUE
+ * @return <code>true</code> if operation was successful, <code>false</code> otherwise.
+ */
+  public static boolean setMaxPostprocess(int max)
+    {
+    return EffectQueue.setMax(EffectTypes.POSTPROCESS.ordinal(),max);
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////   
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // Individual effect functions.
diff --git a/src/main/java/org/distorted/library/DistortedFramebuffer.java b/src/main/java/org/distorted/library/DistortedFramebuffer.java
index e76495b..e5bc891 100644
--- a/src/main/java/org/distorted/library/DistortedFramebuffer.java
+++ b/src/main/java/org/distorted/library/DistortedFramebuffer.java
@@ -273,7 +273,7 @@ public class DistortedFramebuffer
  * <p>
  * Must be called from a thread holding OpenGL Context.
  *
- * @param fbo The Framebuffer (previously created with the first  constructor, drawing FROM the screen
+ * @param fbo The Framebuffer (previously created with the first constructor, drawing FROM the screen
  *            is unsupported!) whose COLOR attachment 0 will be used as input texture.
  *            Please note that rendering from an FBO to itself is unsupported by OpenGL!
  * @param grid Class descendant from GridObject
diff --git a/src/main/java/org/distorted/library/DistortedTexture.java b/src/main/java/org/distorted/library/DistortedTexture.java
index f07228c..21cbafe 100644
--- a/src/main/java/org/distorted/library/DistortedTexture.java
+++ b/src/main/java/org/distorted/library/DistortedTexture.java
@@ -231,7 +231,7 @@ public class DistortedTexture
  * Sets the underlying android.graphics.Bitmap object.
  * <p>
  * You can only recycle() the passed Bitmap once the OpenGL context gets created (i.e. after call
- * to onSurfaceCreated) because only after this point can the Library upload it to the GPU!
+ * to GLSurfaceView.onSurfaceCreated) because only after this point can the Library upload it to the GPU!
  *
  * @param bmp The android.graphics.Bitmap object to apply effects to and display.
  */
diff --git a/src/main/java/org/distorted/library/DistortedTree.java b/src/main/java/org/distorted/library/DistortedTree.java
index 752a26d..7943f7a 100644
--- a/src/main/java/org/distorted/library/DistortedTree.java
+++ b/src/main/java/org/distorted/library/DistortedTree.java
@@ -49,15 +49,15 @@ public class DistortedTree
     {
     long ID;
     int numPointingNodes;
-    DistortedFramebuffer mDF;
     int numRendered;
+    DistortedFramebuffer mDF;
 
     NodeData(long id)
       {
       ID              = id;
       numPointingNodes= 1;
-      mDF             = null;
       numRendered     = 0;
+      mDF             = null;
       }
     }
  
@@ -296,7 +296,7 @@ public class DistortedTree
     {
     mParent = null;
     mTexture= new DistortedTexture(node.mTexture,flags);
-    mEffects = new DistortedEffects(node.mEffects, flags);
+    mEffects= new DistortedEffects(node.mEffects,flags);
     mGrid   = node.mGrid;
 
     if( (flags & Distorted.CLONE_CHILDREN) != 0 )
diff --git a/src/main/java/org/distorted/library/GridObject.java b/src/main/java/org/distorted/library/GridObject.java
index 70bcfa7..c0b9ccd 100644
--- a/src/main/java/org/distorted/library/GridObject.java
+++ b/src/main/java/org/distorted/library/GridObject.java
@@ -55,9 +55,9 @@ public abstract class GridObject
 
    void draw()
      { 
-     GLES20.glVertexAttribPointer(Distorted.mPositionH    , POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, 0, mGridPositions);          
-     GLES20.glVertexAttribPointer(Distorted.mNormalH      , NORMAL_DATA_SIZE  , GLES20.GL_FLOAT, false, 0, mGridNormals  );
-     GLES20.glVertexAttribPointer(Distorted.mTextureCoordH, TEX_DATA_SIZE     , GLES20.GL_FLOAT, false, 0, mGridTexture  );  
+     GLES20.glVertexAttribPointer(Distorted.mAttributes[0], POSITION_DATA_SIZE, GLES20.GL_FLOAT, false, 0, mGridPositions);
+     GLES20.glVertexAttribPointer(Distorted.mAttributes[1], NORMAL_DATA_SIZE  , GLES20.GL_FLOAT, false, 0, mGridNormals  );
+     GLES20.glVertexAttribPointer(Distorted.mAttributes[2], TEX_DATA_SIZE     , GLES20.GL_FLOAT, false, 0, mGridTexture  );
 
      GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, dataLength); 
      }
diff --git a/src/main/java/org/distorted/library/exception/FragmentCompilationException.java b/src/main/java/org/distorted/library/exception/FragmentCompilationException.java
deleted file mode 100644
index e97038c..0000000
--- a/src/main/java/org/distorted/library/exception/FragmentCompilationException.java
+++ /dev/null
@@ -1,79 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted 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 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.exception;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- *  Thrown by {@link org.distorted.library.Distorted#onSurfaceCreated(android.content.Context)}
- *  if compilation of the fragment shader fails for some other reason than too many uniforms.
- *  <p>
- *  This can happen on older OpenGL ES 2.0 devices if they, say, do not support variable loops in the shaders.
- *  Theoretically should never happen on devices supporting at least OpenGL ES 3.0.
- */
-
-@SuppressWarnings("serial")
-public class FragmentCompilationException extends Exception 
-  {
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default empty constructor  
- */
-  public FragmentCompilationException() 
-    {
-   
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor with a message describing why compilation failed.  
- *   
- * @param detailMessage Message describing why compilation failed
- */
-  public FragmentCompilationException(String detailMessage) 
-    {
-    super(detailMessage);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor necessary to make Chained Exceptions working.
- *  
- * @param throwable The parent Throwable.
- */
-  public FragmentCompilationException(Throwable throwable) 
-    {
-    super(throwable);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor necessary to make Chained Exceptions working.
- *   
- * @param detailMessage Message describing why compilation failed
- * @param throwable The parent Throwable.
- */
-  public FragmentCompilationException(String detailMessage, Throwable throwable) 
-    {
-    super(detailMessage, throwable);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////  
-  }
diff --git a/src/main/java/org/distorted/library/exception/FragmentUniformsException.java b/src/main/java/org/distorted/library/exception/FragmentUniformsException.java
deleted file mode 100644
index 21add3f..0000000
--- a/src/main/java/org/distorted/library/exception/FragmentUniformsException.java
+++ /dev/null
@@ -1,103 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted 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 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.exception;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- *  Thrown by {@link org.distorted.library.Distorted#onSurfaceCreated(android.content.Context)}
- *  if compilation of the fragment shader fails because of too many uniforms there, i.e. because
- *  we have set {@link org.distorted.library.Distorted#setMaxFragment(int)} to too high value.
- */
-
-@SuppressWarnings("serial")
-public class FragmentUniformsException extends Exception 
-  {
-  private int max=0;
-    
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default empty constructor  
- */   
-  public FragmentUniformsException() 
-    {
-   
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor with a message describing why compilation failed.  
- *   
- * @param detailMessage Message describing why compilation failed
- */  
-  public FragmentUniformsException(String detailMessage) 
-    {
-    super(detailMessage);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor necessary to make Chained Exceptions working.
- *  
- * @param throwable The parent Throwable.
- */ 
-  public FragmentUniformsException(Throwable throwable) 
-    {
-    super(throwable);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor necessary to make Chained Exceptions working.
- *   
- * @param detailMessage Message describing why compilation failed
- * @param throwable The parent Throwable.
- */  
-  public FragmentUniformsException(String detailMessage, Throwable throwable) 
-    {
-    super(detailMessage, throwable);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor with a message describing why compilation failed and integer holding the maximum
- * number of uniforms in Fragment Shader supported by current hardware.   
- *   
- * @param detailMessage Message describing why compilation failed
- * @param m maximum number of uniforms in Fragment Shader supported by current hardware.   
- */   
-  public FragmentUniformsException(String detailMessage, int m) 
-    {
-    super(detailMessage);
-    max = m;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////  
-/**
- * Gets the maximum number of uniforms in fragment shader supported by current hardware.
- * 
- * @return Maximum number of uniforms in fragment shader supported by current hardware.   
- */
-  public int getMax()
-    {
-    return max;  
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////  
-  }
diff --git a/src/main/java/org/distorted/library/exception/LinkingException.java b/src/main/java/org/distorted/library/exception/LinkingException.java
deleted file mode 100644
index c07ee11..0000000
--- a/src/main/java/org/distorted/library/exception/LinkingException.java
+++ /dev/null
@@ -1,77 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted 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 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.exception;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- *  Thrown by {@link org.distorted.library.Distorted#onSurfaceCreated(android.content.Context)}
- *  if linking of the Shaders fails.
- *  <p>
- *  Theoretically this should never happen.
- */
-@SuppressWarnings("serial")
-public class LinkingException extends Exception 
-  {
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default empty constructor  
- */      
-  public LinkingException() 
-    {
-   
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor with a message describing why linking failed.  
- *   
- * @param detailMessage Message describing why linking failed
- */    
-  public LinkingException(String detailMessage) 
-    {
-    super(detailMessage);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor necessary to make Chained Exceptions working.
- *  
- * @param throwable The parent Throwable.
- */  
-  public LinkingException(Throwable throwable) 
-    {
-    super(throwable);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor necessary to make Chained Exceptions working.
- *   
- * @param detailMessage Message describing why linking failed
- * @param throwable The parent Throwable.
- */      
-  public LinkingException(String detailMessage, Throwable throwable) 
-    {
-    super(detailMessage, throwable);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////  
-  }
diff --git a/src/main/java/org/distorted/library/exception/VertexCompilationException.java b/src/main/java/org/distorted/library/exception/VertexCompilationException.java
deleted file mode 100644
index f3c2372..0000000
--- a/src/main/java/org/distorted/library/exception/VertexCompilationException.java
+++ /dev/null
@@ -1,79 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted 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 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.exception;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- *  Thrown by {@link org.distorted.library.Distorted#onSurfaceCreated(android.content.Context)}
- *  if compilation of the vertex shader fails for some other reason than too many uniforms.
- *  <p>
- *  This can happen on older OpenGL ES 2.0 devices if they, say, do not support variable loops in the shaders.
- *  Theoretically should never happen on devices supporting at least OpenGL ES 3.0.
- */
-
-@SuppressWarnings("serial")
-public class VertexCompilationException extends Exception 
-  {
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default empty constructor  
- */   
-  public VertexCompilationException() 
-    {
-   
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor with a message describing why compilation failed.  
- *   
- * @param detailMessage Message describing why compilation failed
- */  
-  public VertexCompilationException(String detailMessage) 
-    {
-    super(detailMessage);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor necessary to make Chained Exceptions working.
- *  
- * @param throwable The parent Throwable.
- */ 
-  public VertexCompilationException(Throwable throwable) 
-    {
-    super(throwable);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor necessary to make Chained Exceptions working.
- *   
- * @param detailMessage Message describing why compilation failed
- * @param throwable The parent Throwable.
- */  
-  public VertexCompilationException(String detailMessage, Throwable throwable) 
-    {
-    super(detailMessage, throwable);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////  
-  }
diff --git a/src/main/java/org/distorted/library/exception/VertexUniformsException.java b/src/main/java/org/distorted/library/exception/VertexUniformsException.java
deleted file mode 100644
index 5b34008..0000000
--- a/src/main/java/org/distorted/library/exception/VertexUniformsException.java
+++ /dev/null
@@ -1,103 +0,0 @@
-///////////////////////////////////////////////////////////////////////////////////////////////////
-// Copyright 2016 Leszek Koltunski                                                               //
-//                                                                                               //
-// This file is part of Distorted.                                                               //
-//                                                                                               //
-// Distorted 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 2 of the License, or                             //
-// (at your option) any later version.                                                           //
-//                                                                                               //
-// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-package org.distorted.library.exception;
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- *  Thrown by {@link org.distorted.library.Distorted#onSurfaceCreated(android.content.Context)}
- *  if compilation of the Vertex Shader fails because of too many uniforms there, i.e. because
- *  we have set {@link org.distorted.library.Distorted#setMaxVertex(int)} to too high value.
- */
-
-@SuppressWarnings("serial")
-public class VertexUniformsException extends Exception 
-  {
-  private int max=0;
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Default empty constructor  
- */      
-  public VertexUniformsException() 
-    {
-   
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor with a message describing why compilation failed.  
- *   
- * @param detailMessage Message describing why compilation failed
- */   
-  public VertexUniformsException(String detailMessage) 
-    {
-    super(detailMessage);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor necessary to make Chained Exceptions working.
- *  
- * @param throwable The parent Throwable.
- */ 
-  public VertexUniformsException(Throwable throwable) 
-    {
-    super(throwable);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor necessary to make Chained Exceptions working.
- *   
- * @param detailMessage Message describing why compilation failed
- * @param throwable The parent Throwable.
- */    
-  public VertexUniformsException(String detailMessage, Throwable throwable) 
-    {
-    super(detailMessage, throwable);
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-/**
- * Constructor with a message describing why compilation failed and integer holding the maximum
- * number of uniforms in Vertex Shader supported by current hardware.   
- *   
- * @param detailMessage Message describing why compilation failed
- * @param m maximum number of uniforms in Vertex Shader supported by current hardware.   
- */     
-  public VertexUniformsException(String detailMessage, int m) 
-    {
-    super(detailMessage);
-    max = m;
-    }
-
-///////////////////////////////////////////////////////////////////////////////////////////////////  
-/**
- * Gets the maximum number of uniforms in Vertex Shader supported by current hardware.
- * 
- * @return Maximum number of uniforms in Vertex Shader supported by current hardware.   
- */
-  public int getMax()
-    {
-    return max;  
-    }
-  
-///////////////////////////////////////////////////////////////////////////////////////////////////  
-  }
diff --git a/src/main/java/org/distorted/library/program/DistortedProgram.java b/src/main/java/org/distorted/library/program/DistortedProgram.java
new file mode 100644
index 0000000..5950848
--- /dev/null
+++ b/src/main/java/org/distorted/library/program/DistortedProgram.java
@@ -0,0 +1,344 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted 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 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.program;
+
+import android.opengl.GLES20;
+import android.os.Build;
+
+import org.distorted.library.DistortedEffects;
+import org.distorted.library.EffectNames;
+import org.distorted.library.EffectTypes;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * An object which encapsulates a vertex/fragment shader combo, aka Shader Program.
+ */
+public class DistortedProgram
+  {
+  private int mProgramHandle;
+
+  private int mNumAttributes;
+  private int[] mAttribute;
+  private String[] mAttributeName;
+  private String mAttributeTmp;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private int createAndLinkProgram(final int vertexShaderHandle, final int fragmentShaderHandle, final String[] attributes)
+  throws LinkingException
+    {
+    int programHandle = GLES20.glCreateProgram();
+
+    if (programHandle != 0)
+      {
+      GLES20.glAttachShader(programHandle, vertexShaderHandle);
+      GLES20.glAttachShader(programHandle, fragmentShaderHandle);
+
+      if (attributes != null)
+        {
+        final int size = attributes.length;
+
+        for (int i = 0; i < size; i++)
+          {
+          GLES20.glBindAttribLocation(programHandle, i, attributes[i]);
+          }
+        }
+
+      GLES20.glLinkProgram(programHandle);
+
+      final int[] linkStatus = new int[1];
+      GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0);
+
+      if (linkStatus[0] != GLES20.GL_TRUE )
+        {
+        String error = GLES20.glGetProgramInfoLog(programHandle);
+        GLES20.glDeleteProgram(programHandle);
+        throw new LinkingException(error);
+        }
+
+      final int[] numberOfUniforms = new int[1];
+      GLES20.glGetProgramiv(programHandle, GLES20.GL_ACTIVE_UNIFORMS, numberOfUniforms, 0);
+
+      //android.util.Log.d("program", "number of active uniforms="+numberOfUniforms[0]);
+      }
+
+    return programHandle;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void doAttributes(final String line)
+    {
+    int len = line.length();
+    int whiteSpace, semicolon, nameBegin;
+    char currChar;
+
+    for(whiteSpace=0; whiteSpace<len; whiteSpace++)
+      {
+      currChar = line.charAt(whiteSpace);
+      if( currChar!=' ' && currChar!='\t') break;
+      }
+
+    for(semicolon=whiteSpace; semicolon<len; semicolon++)
+      {
+      currChar = line.charAt(semicolon);
+      if( currChar==';') break;
+      }
+
+    if( semicolon<len && semicolon-whiteSpace>=11 )   // "attribute a;" --> 11
+      {
+      String subline = line.substring(whiteSpace,semicolon);
+      int subLen = semicolon-whiteSpace;
+
+      if( subline.startsWith("attribute"))
+        {
+        //android.util.Log.e("program", "GOOD LINE: " +subline+" subLen="+subLen);
+
+        for(nameBegin=subLen-1; nameBegin>8; nameBegin--)
+          {
+          currChar=subline.charAt(nameBegin);
+
+          if( currChar==' ' || currChar=='\t' )
+            {
+            subline = subline.substring(nameBegin+1,subLen);
+            //android.util.Log.d("program", "new attribute: "+subline);
+
+            if( mAttributeTmp.length()>0 ) mAttributeTmp+=" ";
+            mAttributeTmp += subline;
+            mNumAttributes++;
+            break;
+            }
+          }
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private String readTextFileFromRawResource(final InputStream inputStream, boolean doAttributes)
+    {
+    final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
+    final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
+
+    String nextLine;
+    final StringBuilder body = new StringBuilder();
+
+    try
+      {
+      while ((nextLine = bufferedReader.readLine()) != null)
+        {
+        body.append(nextLine);
+        body.append('\n');
+
+        if( doAttributes ) doAttributes(nextLine);
+        }
+      }
+    catch (IOException e)
+      {
+      return null;
+      }
+
+    if( doAttributes )
+      {
+      mAttribute = new int[mNumAttributes];
+      mAttributeName = mAttributeTmp.split(" ");
+      mAttributeTmp = "";
+/*
+      int len = mAttributeName.length;
+
+      for(int i=0; i<len; i++)
+        {
+        android.util.Log.e("program","ATTRIBUTE "+i+" :" + mAttributeName[i]);
+        }
+
+      android.util.Log.e("program","mNumAttributes: "+mNumAttributes);
+*/
+      }
+
+    return body.toString();
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static void sanitizeMaxValues()
+  throws VertexUniformsException,FragmentUniformsException
+    {
+    int maxV,maxF;
+    int[] param = new int[1];
+
+    GLES20.glGetIntegerv(GLES20.GL_MAX_VERTEX_UNIFORM_VECTORS, param, 0);
+    maxV = param[0];
+    GLES20.glGetIntegerv(GLES20.GL_MAX_FRAGMENT_UNIFORM_VECTORS, param, 0);
+    maxF = param[0];
+
+    //Log.d("program", "Max vectors in vertex shader: "+maxV);
+    //Log.d("program", "Max vectors in fragment shader: "+maxF);
+
+    if( !Build.FINGERPRINT.contains("generic") )
+      {
+      int realMaxV = (maxV-11)/4;   // adjust this in case of changes to the shaders...
+      int realMaxF = (maxF- 2)/4;   //
+
+      if( DistortedEffects.getMaxVertex()   > realMaxV )
+        {
+        throw new VertexUniformsException("Too many effects in the vertex shader, max is "+realMaxV, realMaxV);
+        }
+      if( DistortedEffects.getMaxFragment() > realMaxF )
+        {
+        throw new FragmentUniformsException("Too many effects in the fragment shader, max is "+realMaxF, realMaxF);
+        }
+      }
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static int compileShader(final int shaderType, final String shaderSource)
+  throws FragmentCompilationException,VertexCompilationException
+    {
+    int shaderHandle = GLES20.glCreateShader(shaderType);
+
+    if (shaderHandle != 0)
+      {
+      GLES20.glShaderSource(shaderHandle, "#version 100 \n"+ generateShaderHeader(shaderType) + shaderSource);
+      GLES20.glCompileShader(shaderHandle);
+      final int[] compileStatus = new int[1];
+      GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0);
+
+      if (compileStatus[0] != GLES20.GL_TRUE )
+        {
+        GLES20.glDeleteShader(shaderHandle);
+        shaderHandle = 0;
+        }
+      }
+
+    if (shaderHandle == 0)
+      {
+      String error = GLES20.glGetShaderInfoLog(shaderHandle);
+
+      switch(shaderType)
+        {
+        case GLES20.GL_VERTEX_SHADER  : throw new VertexCompilationException(error);
+        case GLES20.GL_FRAGMENT_SHADER: throw new FragmentCompilationException(error);
+        default                       : throw new RuntimeException(error);
+        }
+      }
+
+    return shaderHandle;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private static String generateShaderHeader(final int type)
+    {
+    String header="";
+
+    switch(type)
+      {
+      case GLES20.GL_VERTEX_SHADER  : header += ("#define NUM_VERTEX "  + DistortedEffects.getMaxVertex()+"\n");
+
+                                      for(EffectNames name: EffectNames.values() )
+                                        {
+                                        if( name.getType()== EffectTypes.VERTEX)
+                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");
+                                        }
+                                      break;
+      case GLES20.GL_FRAGMENT_SHADER: header += ("#define NUM_FRAGMENT "+ DistortedEffects.getMaxFragment()+"\n");
+
+                                      for(EffectNames name: EffectNames.values() )
+                                        {
+                                        if( name.getType()==EffectTypes.FRAGMENT)
+                                        header += ("#define "+name.name()+" "+name.ordinal()+"\n");
+                                        }
+                                      break;
+     }
+
+    //Log.d(TAG,""+header);
+
+    return header;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// PUBLIC API
+///////////////////////////////////////////////////////////////////////////////////////////////////
+  /**
+   * Create a new Shader Program from source stored in resource files.
+   * <p>
+   * Needs to be called from a thread holding the OpenGL context.
+   *
+   * @param vertex   InputStream containing the opened Resource file from where to read vertex shader code.
+   * @param fragment InputStream containing the opened Resource file from where to read fragment shader code.
+   *
+   */
+
+  public DistortedProgram(final InputStream vertex, final InputStream fragment)
+  throws FragmentCompilationException,VertexCompilationException,VertexUniformsException,FragmentUniformsException,LinkingException
+    {
+    mNumAttributes = 0;
+    mAttributeTmp  = "";
+
+    final String vertexShader   = readTextFileFromRawResource(vertex  , true );
+    final String fragmentShader = readTextFileFromRawResource(fragment, false);
+
+    sanitizeMaxValues();
+
+    final int vertexShaderHandle   = compileShader(GLES20.GL_VERTEX_SHADER  , vertexShader  );
+    final int fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShader);
+
+    mProgramHandle = createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle, mAttributeName);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int[] getAttributes()
+    {
+    return mAttribute;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public String[] getAttributeNames()
+    {
+    return mAttributeName;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public int getProgramHandle()
+    {
+    return mProgramHandle;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// glUseProgram first; needs to be called from a thread holding the OpenGL context
+
+  public void bindAndEnableAttributes()
+    {
+    for(int i=0; i<mNumAttributes; i++)
+      {
+      mAttribute[i] = GLES20.glGetAttribLocation( mProgramHandle, mAttributeName[i]);
+      GLES20.glEnableVertexAttribArray(mAttribute[i]);
+      }
+    }
+  }
diff --git a/src/main/java/org/distorted/library/program/FragmentCompilationException.java b/src/main/java/org/distorted/library/program/FragmentCompilationException.java
new file mode 100644
index 0000000..ff78a08
--- /dev/null
+++ b/src/main/java/org/distorted/library/program/FragmentCompilationException.java
@@ -0,0 +1,79 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted 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 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.program;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ *  Thrown by {@link org.distorted.library.Distorted#onCreate(android.content.Context)}
+ *  if compilation of the fragment shader fails for some other reason than too many uniforms.
+ *  <p>
+ *  This can happen on older OpenGL ES 2.0 devices if they, say, do not support variable loops in the shaders.
+ *  Theoretically should never happen on devices supporting at least OpenGL ES 3.0.
+ */
+
+@SuppressWarnings("serial")
+public class FragmentCompilationException extends Exception 
+  {
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default empty constructor  
+ */
+  public FragmentCompilationException() 
+    {
+   
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor with a message describing why compilation failed.  
+ *   
+ * @param detailMessage Message describing why compilation failed
+ */
+  public FragmentCompilationException(String detailMessage) 
+    {
+    super(detailMessage);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor necessary to make Chained Exceptions working.
+ *  
+ * @param throwable The parent Throwable.
+ */
+  public FragmentCompilationException(Throwable throwable) 
+    {
+    super(throwable);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor necessary to make Chained Exceptions working.
+ *   
+ * @param detailMessage Message describing why compilation failed
+ * @param throwable The parent Throwable.
+ */
+  public FragmentCompilationException(String detailMessage, Throwable throwable) 
+    {
+    super(detailMessage, throwable);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////  
+  }
diff --git a/src/main/java/org/distorted/library/program/FragmentUniformsException.java b/src/main/java/org/distorted/library/program/FragmentUniformsException.java
new file mode 100644
index 0000000..95f64d1
--- /dev/null
+++ b/src/main/java/org/distorted/library/program/FragmentUniformsException.java
@@ -0,0 +1,103 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted 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 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.program;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ *  Thrown by {@link org.distorted.library.Distorted#onCreate(android.content.Context)}
+ *  if compilation of the fragment shader fails because of too many uniforms there, i.e. because
+ *  we have set {@link org.distorted.library.Distorted#setMaxFragment(int)} to too high value.
+ */
+
+@SuppressWarnings("serial")
+public class FragmentUniformsException extends Exception 
+  {
+  private int max=0;
+    
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default empty constructor  
+ */   
+  public FragmentUniformsException() 
+    {
+   
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor with a message describing why compilation failed.  
+ *   
+ * @param detailMessage Message describing why compilation failed
+ */  
+  public FragmentUniformsException(String detailMessage) 
+    {
+    super(detailMessage);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor necessary to make Chained Exceptions working.
+ *  
+ * @param throwable The parent Throwable.
+ */ 
+  public FragmentUniformsException(Throwable throwable) 
+    {
+    super(throwable);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor necessary to make Chained Exceptions working.
+ *   
+ * @param detailMessage Message describing why compilation failed
+ * @param throwable The parent Throwable.
+ */  
+  public FragmentUniformsException(String detailMessage, Throwable throwable) 
+    {
+    super(detailMessage, throwable);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor with a message describing why compilation failed and integer holding the maximum
+ * number of uniforms in Fragment Shader supported by current hardware.   
+ *   
+ * @param detailMessage Message describing why compilation failed
+ * @param m maximum number of uniforms in Fragment Shader supported by current hardware.   
+ */   
+  public FragmentUniformsException(String detailMessage, int m) 
+    {
+    super(detailMessage);
+    max = m;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////  
+/**
+ * Gets the maximum number of uniforms in fragment shader supported by current hardware.
+ * 
+ * @return Maximum number of uniforms in fragment shader supported by current hardware.   
+ */
+  public int getMax()
+    {
+    return max;  
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////  
+  }
diff --git a/src/main/java/org/distorted/library/program/LinkingException.java b/src/main/java/org/distorted/library/program/LinkingException.java
new file mode 100644
index 0000000..f14e497
--- /dev/null
+++ b/src/main/java/org/distorted/library/program/LinkingException.java
@@ -0,0 +1,77 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted 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 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.program;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ *  Thrown by {@link org.distorted.library.Distorted#onCreate(android.content.Context)}
+ *  if linking of the Shaders fails.
+ *  <p>
+ *  Theoretically this should never happen.
+ */
+@SuppressWarnings("serial")
+public class LinkingException extends Exception 
+  {
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default empty constructor  
+ */      
+  public LinkingException() 
+    {
+   
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor with a message describing why linking failed.  
+ *   
+ * @param detailMessage Message describing why linking failed
+ */    
+  public LinkingException(String detailMessage) 
+    {
+    super(detailMessage);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor necessary to make Chained Exceptions working.
+ *  
+ * @param throwable The parent Throwable.
+ */  
+  public LinkingException(Throwable throwable) 
+    {
+    super(throwable);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor necessary to make Chained Exceptions working.
+ *   
+ * @param detailMessage Message describing why linking failed
+ * @param throwable The parent Throwable.
+ */      
+  public LinkingException(String detailMessage, Throwable throwable) 
+    {
+    super(detailMessage, throwable);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////  
+  }
diff --git a/src/main/java/org/distorted/library/program/VertexCompilationException.java b/src/main/java/org/distorted/library/program/VertexCompilationException.java
new file mode 100644
index 0000000..ac8b1c5
--- /dev/null
+++ b/src/main/java/org/distorted/library/program/VertexCompilationException.java
@@ -0,0 +1,79 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted 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 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.program;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ *  Thrown by {@link org.distorted.library.Distorted#onCreate(android.content.Context)}
+ *  if compilation of the vertex shader fails for some other reason than too many uniforms.
+ *  <p>
+ *  This can happen on older OpenGL ES 2.0 devices if they, say, do not support variable loops in the shaders.
+ *  Theoretically should never happen on devices supporting at least OpenGL ES 3.0.
+ */
+
+@SuppressWarnings("serial")
+public class VertexCompilationException extends Exception 
+  {
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default empty constructor  
+ */   
+  public VertexCompilationException() 
+    {
+   
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor with a message describing why compilation failed.  
+ *   
+ * @param detailMessage Message describing why compilation failed
+ */  
+  public VertexCompilationException(String detailMessage) 
+    {
+    super(detailMessage);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor necessary to make Chained Exceptions working.
+ *  
+ * @param throwable The parent Throwable.
+ */ 
+  public VertexCompilationException(Throwable throwable) 
+    {
+    super(throwable);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor necessary to make Chained Exceptions working.
+ *   
+ * @param detailMessage Message describing why compilation failed
+ * @param throwable The parent Throwable.
+ */  
+  public VertexCompilationException(String detailMessage, Throwable throwable) 
+    {
+    super(detailMessage, throwable);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////  
+  }
diff --git a/src/main/java/org/distorted/library/program/VertexUniformsException.java b/src/main/java/org/distorted/library/program/VertexUniformsException.java
new file mode 100644
index 0000000..da02370
--- /dev/null
+++ b/src/main/java/org/distorted/library/program/VertexUniformsException.java
@@ -0,0 +1,103 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// Copyright 2016 Leszek Koltunski                                                               //
+//                                                                                               //
+// This file is part of Distorted.                                                               //
+//                                                                                               //
+// Distorted 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 2 of the License, or                             //
+// (at your option) any later version.                                                           //
+//                                                                                               //
+// Distorted 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 Distorted.  If not, see <http://www.gnu.org/licenses/>.                            //
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+package org.distorted.library.program;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ *  Thrown by {@link org.distorted.library.Distorted#onCreate(android.content.Context)}
+ *  if compilation of the Vertex Shader fails because of too many uniforms there, i.e. because
+ *  we have set {@link org.distorted.library.Distorted#setMaxVertex(int)} to too high value.
+ */
+
+@SuppressWarnings("serial")
+public class VertexUniformsException extends Exception 
+  {
+  private int max=0;
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Default empty constructor  
+ */      
+  public VertexUniformsException() 
+    {
+   
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor with a message describing why compilation failed.  
+ *   
+ * @param detailMessage Message describing why compilation failed
+ */   
+  public VertexUniformsException(String detailMessage) 
+    {
+    super(detailMessage);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor necessary to make Chained Exceptions working.
+ *  
+ * @param throwable The parent Throwable.
+ */ 
+  public VertexUniformsException(Throwable throwable) 
+    {
+    super(throwable);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor necessary to make Chained Exceptions working.
+ *   
+ * @param detailMessage Message describing why compilation failed
+ * @param throwable The parent Throwable.
+ */    
+  public VertexUniformsException(String detailMessage, Throwable throwable) 
+    {
+    super(detailMessage, throwable);
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Constructor with a message describing why compilation failed and integer holding the maximum
+ * number of uniforms in Vertex Shader supported by current hardware.   
+ *   
+ * @param detailMessage Message describing why compilation failed
+ * @param m maximum number of uniforms in Vertex Shader supported by current hardware.   
+ */     
+  public VertexUniformsException(String detailMessage, int m) 
+    {
+    super(detailMessage);
+    max = m;
+    }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////  
+/**
+ * Gets the maximum number of uniforms in Vertex Shader supported by current hardware.
+ * 
+ * @return Maximum number of uniforms in Vertex Shader supported by current hardware.   
+ */
+  public int getMax()
+    {
+    return max;  
+    }
+  
+///////////////////////////////////////////////////////////////////////////////////////////////////  
+  }
