All these steps require several image copies and lots of calculation. The result is good, but the time it takes to create can be long, because, on big images, copying pixels from images to images can be costly. Why don't we work only at the pixel level and avoid image copies ?

The following code explains how reflection effect can be optimized by working directly on pixels values :

 public void applyFilter(int[] srcPixels, int width, int height) {
     reflectionHeight = reflectionHeight == 0 ? height/2 : reflectionHeight;
     for (int i = 0; i < height; i++) {
         if(i >= reflectionHeight) {
             for(int j = 0; j < width; j++) {
                 // the rest of the image is transparent
                 srcPixels`[`(i`*`width)+j`]` = 0x00; 
             }
         } else {
              for(int j = 0; j < width; j++) {
                 int srcARGB = srcPixels[((height - i - 1)*width)+j];
	         int srcR = (srcARGB >> 16) & 0xff;
	         int srcG = (srcARGB >> 8) & 0xff;
	         int srcB = srcARGB & 0xff;
	         int srcA = srcARGB >>> 24;
	         // calculate the alpha gradient and divide the result by the specified divider.
	         int maskA = (0xff - (i*0xff)/reflectionHeight) >> fadingDivider;
	         // DST_IN blend mode
	         srcR = (srcR * maskA) >> 8; 
	         srcG = (srcG * maskA) >> 8;
	         srcB = (srcB * maskA) >> 8;
	         srcA = (srcA * maskA) >> 8;
	         srcPixels[(i*width)+j] = (srcA << 24) | (srcR << 16) | (srcG << 8) | srcB; 
             }
        }
   }
}

This filter is available in PulpCore (in contrib section in the nightly build for now).

it's also available here.

An example of use in PulpCore :

 CoreImage duplicateImage(CoreImage img) {
		 // opaque is false because the reflection image is in part translucent
		CoreImage result = new CoreImage(img.getWidth(), img.getHeight(), false);
		CoreGraphics g = result.createGraphics();
		g.drawImage(img);
		return result;
	 }
	 
	@Override
	public void load() {

		add(new FilledSprite(Colors.WHITE));
		
		CoreImage img = CoreImage.load("monumentalsimplicity.png");
		simpl = new ImageSprite(img, 30,30);
		add(simpl);
		
		ImageSprite refl = new ImageSprite(duplicateImage(img), 30, 300);
		Filter f = new ReflectionFilter(130, FADE_BY_2);
		f.applyFilter(refl.getImage().getData(), img.getWidth(), img.getHeight());
		add(refl);
	}