commit a31dbc5cd31c2797104e212c0ae194076dd8ad1e
Author: Leszek Koltunski <leszek@distoretedandroid.org>
Date:   Fri May 26 15:09:37 2017 +0100

    Beginnings of support for the GLOW effect.

diff --git a/src/main/java/org/distorted/library/DistortedEffectsPostprocess.java b/src/main/java/org/distorted/library/DistortedEffectsPostprocess.java
index db09afa..c652622 100644
--- a/src/main/java/org/distorted/library/DistortedEffectsPostprocess.java
+++ b/src/main/java/org/distorted/library/DistortedEffectsPostprocess.java
@@ -21,6 +21,7 @@ package org.distorted.library;
 
 import org.distorted.library.message.EffectListener;
 import org.distorted.library.type.Data1D;
+import org.distorted.library.type.Data4D;
 
 import java.util.ArrayList;
 
@@ -371,4 +372,18 @@ public class DistortedEffectsPostprocess implements DistortedSlave
     {
     return mP.add(EffectNames.BLUR, radius);
     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * Make the object glow with a specific color and a halo of specific radius.
+ *
+ * @param radius The 'strength' if the effect, in pixels. 0 = no halo, 10 = halo of roughly 10 pixels
+ *               around the whole object.
+ * @param color  RGBA of the color with which to draw the glow.
+ * @return ID of the effect added, or -1 if we failed to add one.
+ */
+  public long glow(Data1D radius, Data4D color)
+    {
+    return mP.add(EffectNames.GLOW, radius, color);
+    }
   }
diff --git a/src/main/java/org/distorted/library/EffectNames.java b/src/main/java/org/distorted/library/EffectNames.java
index adea44d..83ef78d 100644
--- a/src/main/java/org/distorted/library/EffectNames.java
+++ b/src/main/java/org/distorted/library/EffectNames.java
@@ -241,20 +241,28 @@ public enum EffectNames
 
   /////////////////////////////////////////////////////////////////////////////////
   // POSTPROCESSING EFFECTS.
-  // Always 4 Uniforms: 4 per-effect interpolated values.
+  // Always 5 Uniforms: 5 per-effect interpolated values.
  /**
    * Blur the area around the center.
    * <p>
-   * Uniforms: (radius,UNUSED,UNUSED,UNUSED)
+   * Uniforms: (radius,UNUSED,UNUSED,UNUSED,UNUSED)
    * <p>
    * Unity: radius = 0
    */
-  BLUR             ( EffectTypes.POSTPROCESS,new float[] {0.0f}          , 1, false, false );
+  BLUR             ( EffectTypes.POSTPROCESS,new float[] {0.0f}          , 1, false, false ),
+/**
+   * Make the object Glow with a certain color and configurable radius of the halo around it.
+   * <p>
+   * Uniforms: (A,R,G,B,radius)
+   * <p>
+   * Unity: radius = 0
+   */
+  GLOW             ( EffectTypes.POSTPROCESS,new float[] {0.0f}          , 5, false, false );
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
   
-  private static final int MAXDIM = 4;  // maximum supported dimension of an effect  
-  
+  private static final int MAXDIM = 4;  // maximum supported dimension of the Unity of an Effect
+                                        // (not to be confused with dimension of the Effect itself!)
   private final EffectTypes type;
   private final float[] unity;
   private final int dimension;
diff --git a/src/main/java/org/distorted/library/EffectQueuePostprocess.java b/src/main/java/org/distorted/library/EffectQueuePostprocess.java
index 700aa22..64507cd 100644
--- a/src/main/java/org/distorted/library/EffectQueuePostprocess.java
+++ b/src/main/java/org/distorted/library/EffectQueuePostprocess.java
@@ -30,8 +30,11 @@ import org.distorted.library.program.LinkingException;
 import org.distorted.library.program.VertexCompilationException;
 import org.distorted.library.program.VertexUniformsException;
 import org.distorted.library.type.Data1D;
+import org.distorted.library.type.Data4D;
 import org.distorted.library.type.Dynamic1D;
+import org.distorted.library.type.Dynamic4D;
 import org.distorted.library.type.Static1D;
+import org.distorted.library.type.Static4D;
 
 import java.io.InputStream;
 import java.nio.ByteBuffer;
@@ -42,10 +45,12 @@ import java.nio.FloatBuffer;
 
 class EffectQueuePostprocess extends EffectQueue
   {
+  private static final int MAX_HALO = 50;    // Support effects creating up to MAX_HALO pixels wide 'halo' around the object.
+
   private static final int POS_DATA_SIZE= 2; // Post Program: size of the position data in elements
   private static final int TEX_DATA_SIZE= 2; // Post Program: size of the texture coordinate data in elements.
 
-  private static final int NUM_UNIFORMS = 4;
+  private static final int NUM_UNIFORMS = 5;
   private static final int NUM_CACHE    = 0;
   private static final int INDEX = EffectTypes.POSTPROCESS.ordinal();
 
@@ -68,6 +73,10 @@ class EffectQueuePostprocess extends EffectQueue
     mQuadTextureInv.put(textureInv).position(0);
     }
 
+  int mQualityLevel;
+  float mQualityScale;
+
+  /////////////////////////////////////////////////////////////////////////////////
   // BLUR effect
   private static final float GAUSSIAN[] =   // G(0.00), G(0.03), G(0.06), ..., G(3.00), 0
     {                                       // where G(x)= (1/(sqrt(2*PI))) * e^(-(x^2)/2). The last 0 terminates.
@@ -85,25 +94,21 @@ class EffectQueuePostprocess extends EffectQueue
     };
   private static final int NUM_GAUSSIAN = GAUSSIAN.length-2;
 
-  // Support blurs consisting of the present pixel and up to MAX_BLUR pixels in each direction
-  static final int MAX_BLUR = 50;
-
   // The (fixed-function-sampled) Gaussian Blur kernels are of the size k0=1, k1=2, k2=2, k3=3, k4=3, k5=4, k6=4,...
   // i.e. k(i)=floor((i+3)/2).  (the 'i' in k(i) means 'blur taking into account the present pixel and 'i' pixels
   // in all 4 directions)
   // We need room for MAX_BLUR of them, and sum(i=0...N, floor((i+3)/2)) = N + floor(N*N/4)
-  private static float[] weightsCache = new float[MAX_BLUR + MAX_BLUR*MAX_BLUR/4];
-  private static float[] offsetsCache = new float[MAX_BLUR + MAX_BLUR*MAX_BLUR/4];
+  private static float[] weightsCache = new float[MAX_HALO + MAX_HALO*MAX_HALO/4];
+  private static float[] offsetsCache = new float[MAX_HALO + MAX_HALO*MAX_HALO/4];
 
   private static DistortedProgram mBlur1Program, mBlur2Program;
   private static int mRadius1H,mOffsets1H,mWeights1H,mDepth1H, mColorTexture1H;
   private static int mRadius2H,mOffsets2H,mWeights2H,mDepth2H, mColorTexture2H;
-  private static float[] mWeights = new float[MAX_BLUR];
-  private static float[] mOffsets = new float[MAX_BLUR];
-  // another effect ....
+  private static float[] mWeights = new float[MAX_HALO];
+  private static float[] mOffsets = new float[MAX_HALO];
+  /////////////////////////////////////////////////////////////////////////////////
+  // GLOW effect
 
-  int mQualityLevel;
-  float mQualityScale;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -127,7 +132,7 @@ class EffectQueuePostprocess extends EffectQueue
       {
       mBlur1Program = new DistortedProgram(blur1VertexStream,blur1FragmentStream,
                                           Distorted.GLSL_VERSION,
-                                          Distorted.GLSL_VERSION + "#define MAX_BLUR "+MAX_BLUR, Distorted.GLSL);
+                                          Distorted.GLSL_VERSION + "#define MAX_BLUR "+MAX_HALO, Distorted.GLSL);
       }
     catch(Exception e)
       {
@@ -149,7 +154,7 @@ class EffectQueuePostprocess extends EffectQueue
       {
       mBlur2Program = new DistortedProgram(blur2VertexStream,blur2FragmentStream,
                                           Distorted.GLSL_VERSION,
-                                          Distorted.GLSL_VERSION + "#define MAX_BLUR "+MAX_BLUR, Distorted.GLSL);
+                                          Distorted.GLSL_VERSION + "#define MAX_BLUR "+MAX_HALO, Distorted.GLSL);
       }
     catch(Exception e)
       {
@@ -168,7 +173,7 @@ class EffectQueuePostprocess extends EffectQueue
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  synchronized boolean compute(long currTime)
+  private boolean compute(long currTime)
     {
     if( currTime==mTime ) return false;
     if( mTime==0 ) mTime = currTime;
@@ -195,6 +200,8 @@ class EffectQueuePostprocess extends EffectQueue
           }
         else mInter[0][i] = null;
         }
+
+      if( mInter[1][i]!=null ) mInter[1][i].interpolateMain( mUniforms, NUM_UNIFORMS*i+1, mCurrentDuration[i], step);
       }
 
     mTime = currTime;
@@ -210,6 +217,7 @@ class EffectQueuePostprocess extends EffectQueue
     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];
+    mUniforms[NUM_UNIFORMS*index+4] = mUniforms[NUM_UNIFORMS*(index+1)+4];
     }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -280,7 +288,12 @@ class EffectQueuePostprocess extends EffectQueue
         {
         if (mName[i] == EffectNames.BLUR.ordinal() )
           {
-          blur(mUniforms[i],surface);
+          blur(NUM_UNIFORMS*i,surface);
+          numRenders += 2;
+          }
+        else if (mName[i] == EffectNames.GLOW.ordinal() )
+          {
+          glow(NUM_UNIFORMS*i,surface);
           numRenders += 2;
           }
         }
@@ -291,7 +304,7 @@ class EffectQueuePostprocess extends EffectQueue
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-  private void blur(float degree, DistortedOutputSurface surface)
+  private void blur(int index, DistortedOutputSurface surface)
     {
     DistortedFramebuffer buffer = surface.mBuffer[mQualityLevel];
     GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, buffer.mFBOH[0]);
@@ -299,8 +312,8 @@ class EffectQueuePostprocess extends EffectQueue
     float w1 = buffer.mWidth;
     float h1 = buffer.mHeight;
 
-    int radius = (int)(degree*mQualityScale);
-    if( radius>=MAX_BLUR ) radius = MAX_BLUR-1;
+    int radius = (int)(mUniforms[index]*mQualityScale);
+    if( radius>=MAX_HALO ) radius = MAX_HALO-1;
     computeGaussianKernel(radius);
 
     int offset = radius + radius*radius/4;
@@ -354,6 +367,13 @@ class EffectQueuePostprocess extends EffectQueue
     GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);
     }
 
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+  private void glow(int index, DistortedOutputSurface surface)
+    {
+
+    }
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 // blur
 
@@ -380,4 +400,45 @@ class EffectQueuePostprocess extends EffectQueue
       
     return -1;
     }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// glow
+
+  synchronized long add(EffectNames eln, Data1D degree, Data4D color)
+    {
+    if( mMax[INDEX]>mNumEffects )
+      {
+      if( degree instanceof Dynamic1D)
+        {
+        mInter[0][mNumEffects] = (Dynamic1D)degree;
+        }
+      else if( degree instanceof Static1D)
+        {
+        mInter[0][mNumEffects] = null;
+        mUniforms[NUM_UNIFORMS*mNumEffects] = ((Static1D)degree).getX();
+        }
+      else return -1;
+
+      if( color instanceof Dynamic4D)
+        {
+        mInter[1][mNumEffects] = (Dynamic4D)color;
+        }
+      else if( color instanceof Static4D)
+        {
+        mInter[1][mNumEffects] = null;
+        Static4D tmp = (Static4D)color;
+        mUniforms[NUM_UNIFORMS*mNumEffects+1] = tmp.getW();  //
+        mUniforms[NUM_UNIFORMS*mNumEffects+2] = tmp.getX();  // Invert: RGBA sent
+        mUniforms[NUM_UNIFORMS*mNumEffects+3] = tmp.getY();  // in, ARGB inside
+        mUniforms[NUM_UNIFORMS*mNumEffects+4] = tmp.getZ();  //
+        }
+      else return -1;
+
+      mInter[2][mNumEffects] = null;
+
+      return addBase(eln);
+      }
+
+    return -1;
+    }
   }
