picasso-transformations/src/main/java/com/picassotransformations/jhlabs/ExposureTransformation.java
public class ExposureTransformation extends TransferTransformation {

    private float exposure = 1.0f;

    protected float transferFunction(float f) {
        return 1 - (float) Math.exp(-f * exposure);
    }

    /**
     * Set the exposure level.
     * 
     * @param exposure the exposure level
     * @min-value 0
     * @max-value 5+
     * @see #getExposure
     */
    public void setExposure(float exposure) {
        this.exposure = exposure;
        initialized = false;
    }

    /**
     * Get the exposure level.
     * 
     * @return the exposure level
     * @see #setExposure
     */
    public float getExposure() {
        return exposure;
    }

    public String toString() {
        return "Colors/Exposure...";
    }

    @Override
    public String key() {
        return ExposureTransformation.class.getCanonicalName() + "-" + exposure;
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/CurvesTransformation.java
public class CurvesTransformation extends TransferTransformation {

    private Curve[] curves = new Curve[1];

    public CurvesTransformation() {
        curves = new Curve[3];
        curves[0] = new Curve();
        curves[1] = new Curve();
        curves[2] = new Curve();
    }

    protected void initialize() {
        initialized = true;
        if (curves.length == 1)
            rTable = gTable = bTable = curves[0].makeTable();
        else {
            rTable = curves[0].makeTable();
            gTable = curves[1].makeTable();
            bTable = curves[2].makeTable();
        }
    }

    public void setCurve(Curve curve) {
        curves = new Curve[] {
            curve
        };
        initialized = false;
    }

    public void setCurves(Curve[] curves) {
        if (curves == null || (curves.length != 1 && curves.length != 3))
            throw new IllegalArgumentException("Curves must be length 1 or 3");
        this.curves = curves;
        initialized = false;
    }

    public Curve[] getCurves() {
        return curves;
    }

    public String toString() {
        return "Colors/Curves...";
    }

    @Override
    public String key() {
        return CurvesTransformation.class.getCanonicalName() + "-" + curves.hashCode();
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/EdgeTransformation.java
public class EdgeTransformation extends WholeImageTransformation {
	
	public final static float R2 = (float)Math.sqrt(2);

	public final static float[] ROBERTS_V = {
		0,  0, -1,
		0,  1,  0,
		0,  0,  0,
	};
	public final static float[] ROBERTS_H = {
		-1,  0,  0,
		0,  1,  0,
		0,  0,  0,
	};
	public final static float[] PREWITT_V = {
		-1,  0,  1,
		-1,  0,  1,
		-1,  0,  1,
	};
	public final static float[] PREWITT_H = {
		-1, -1, -1,
		0,  0,  0,
		1,  1,  1,
	};
	public final static float[] SOBEL_V = {
		-1,  0,  1,
		-2,  0,  2,
		-1,  0,  1,
	};
	public static float[] SOBEL_H = {
		-1, -2, -1,
		0,  0,  0,
		1,  2,  1,
	};
	public final static float[] FREI_CHEN_V = {
		-1,  0,  1,
		-R2,  0,  R2,
		-1,  0,  1,
	};
	public static float[] FREI_CHEN_H = {
		-1, -R2, -1,
		0,  0,  0,
		1,  R2,  1,
	};

	protected float[] vEdgeMatrix = SOBEL_V;
	protected float[] hEdgeMatrix = SOBEL_H;

	public EdgeTransformation() {
	}

	public void setVEdgeMatrix(float[] vEdgeMatrix) {
		this.vEdgeMatrix = vEdgeMatrix;
	}

	public float[] getVEdgeMatrix() {
		return vEdgeMatrix;
	}

	public void setHEdgeMatrix(float[] hEdgeMatrix) {
		this.hEdgeMatrix = hEdgeMatrix;
	}

	public float[] getHEdgeMatrix() {
		return hEdgeMatrix;
	}

	protected int[] filterPixels( int width, int height, int[] inPixels, Rect transformedSpace ) {
		int index = 0;
		int[] outPixels = new int[width * height];

		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				int r = 0, g = 0, b = 0;
				int rh = 0, gh = 0, bh = 0;
				int rv = 0, gv = 0, bv = 0;
				int a = inPixels[y*width+x] & 0xff000000;

				for (int row = -1; row <= 1; row++) {
					int iy = y+row;
					int ioffset;
					if (0 <= iy && iy < height)
						ioffset = iy*width;
					else
						ioffset = y*width;
					int moffset = 3*(row+1)+1;
					for (int col = -1; col <= 1; col++) {
						int ix = x+col;
						if (!(0 <= ix && ix < width))
							ix = x;
						int rgb = inPixels[ioffset+ix];
						float h = hEdgeMatrix[moffset+col];
						float v = vEdgeMatrix[moffset+col];

						r = (rgb & 0xff0000) >> 16;
						g = (rgb & 0x00ff00) >> 8;
						b = rgb & 0x0000ff;
						rh += (int)(h * r);
						gh += (int)(h * g);
						bh += (int)(h * b);
						rv += (int)(v * r);
						gv += (int)(v * g);
						bv += (int)(v * b);
					}
				}
				r = (int)(Math.sqrt(rh*rh + rv*rv) / 1.8);
				g = (int)(Math.sqrt(gh*gh + gv*gv) / 1.8);
				b = (int)(Math.sqrt(bh*bh + bv*bv) / 1.8);
				r = PixelUtils.clamp(r);
				g = PixelUtils.clamp(g);
				b = PixelUtils.clamp(b);
				outPixels[index++] = a | (r << 16) | (g << 8) | b;
			}

		}
		return outPixels;
	}

	public String toString() {
		return "Edges/Detect Edges";
	}

    @Override
    public String key() {
        return EdgeTransformation.class.getCanonicalName();
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/QuantizeTransformation.java
public class QuantizeTransformation extends WholeImageTransformation {

    /**
     * Floyd-Steinberg dithering matrix.
     */
    protected final static int[] matrix = {
            0, 0, 0, 0, 0, 7, 3, 5, 1,
    };

    private int sum = 3 + 5 + 7 + 1;

    private boolean dither;

    private int numColors = 256;

    private boolean serpentine = true;

    /**
     * Set the number of colors to quantize to.
     * 
     * @param numColors the number of colors. The default is 256.
     */
    public void setNumColors(int numColors) {
        this.numColors = Math.min(Math.max(numColors, 8), 256);
    }

    /**
     * Get the number of colors to quantize to.
     * 
     * @return the number of colors.
     */
    public int getNumColors() {
        return numColors;
    }

    /**
     * Set whether to use dithering or not. If not, the image is posterized.
     * 
     * @param dither true to use dithering
     */
    public void setDither(boolean dither) {
        this.dither = dither;
    }

    /**
     * Return the dithering setting
     * 
     * @return the current setting
     */
    public boolean getDither() {
        return dither;
    }

    /**
     * Set whether to use a serpentine pattern for return or not. This can reduce 'avalanche' artifacts in the output.
     * 
     * @param serpentine true to use serpentine pattern
     */
    public void setSerpentine(boolean serpentine) {
        this.serpentine = serpentine;
    }

    /**
     * Return the serpentine setting
     * 
     * @return the current setting
     */
    public boolean getSerpentine() {
        return serpentine;
    }

    public void quantize(int[] inPixels, int[] outPixels, int width, int height, int numColors, boolean dither, boolean serpentine) {
        int count = width * height;
        Quantizer quantizer = new OctTreeQuantizer();
        quantizer.setup(numColors);
        quantizer.addPixels(inPixels, 0, count);
        int[] table = quantizer.buildColorTable();

        if (!dither) {
            for (int i = 0; i < count; i++)
                outPixels[i] = table[quantizer.getIndexForColor(inPixels[i])];
        } else {
            int index = 0;
            for (int y = 0; y < height; y++) {
                boolean reverse = serpentine && (y & 1) == 1;
                int direction;
                if (reverse) {
                    index = y * width + width - 1;
                    direction = -1;
                } else {
                    index = y * width;
                    direction = 1;
                }
                for (int x = 0; x < width; x++) {
                    int rgb1 = inPixels[index];
                    int rgb2 = table[quantizer.getIndexForColor(rgb1)];

                    outPixels[index] = rgb2;

                    int r1 = (rgb1 >> 16) & 0xff;
                    int g1 = (rgb1 >> 8) & 0xff;
                    int b1 = rgb1 & 0xff;

                    int r2 = (rgb2 >> 16) & 0xff;
                    int g2 = (rgb2 >> 8) & 0xff;
                    int b2 = rgb2 & 0xff;

                    int er = r1 - r2;
                    int eg = g1 - g2;
                    int eb = b1 - b2;

                    for (int i = -1; i <= 1; i++) {
                        int iy = i + y;
                        if (0 <= iy && iy < height) {
                            for (int j = -1; j <= 1; j++) {
                                int jx = j + x;
                                if (0 <= jx && jx < width) {
                                    int w;
                                    if (reverse)
                                        w = matrix[ (i + 1) * 3 - j + 1];
                                    else
                                        w = matrix[ (i + 1) * 3 + j + 1];
                                    if (w != 0) {
                                        int k = reverse ? index - j : index + j;
                                        rgb1 = inPixels[k];
                                        r1 = (rgb1 >> 16) & 0xff;
                                        g1 = (rgb1 >> 8) & 0xff;
                                        b1 = rgb1 & 0xff;
                                        r1 += er * w / sum;
                                        g1 += eg * w / sum;
                                        b1 += eb * w / sum;
                                        inPixels[k] = (PixelUtils.clamp(r1) << 16) | (PixelUtils.clamp(g1) << 8) | PixelUtils.clamp(b1);
                                    }
                                }
                            }
                        }
                    }
                    index += direction;
                }
            }
        }
    }

    @Override
    protected int[] filterPixels(int width, int height, int[] inPixels, Rect transformedSpace) {
        int[] outPixels = new int[width * height];

        quantize(inPixels, outPixels, width, height, numColors, dither, serpentine);

        return outPixels;
    }

    public String toString() {
        return "Colors/Quantize...";
    }

    @Override
    public String key() {
        return QuantizeTransformation.class.getCanonicalName() + "-" + matrix.hashCode() + "-" + sum + "-" + dither + "-" + numColors + "-"
                + serpentine;
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/LookupTransformation.java
public class LookupTransformation extends PointTransformation {

    private Colormap colormap = new Gradient();

    /**
     * Construct a LookupFilter.
     */
    public LookupTransformation() {
        canFilterIndexColorModel = true;
    }

    /**
     * Construct a LookupFilter.
     * 
     * @param colormap the color map
     */
    public LookupTransformation(Colormap colormap) {
        canFilterIndexColorModel = true;
        this.colormap = colormap;
    }

    /**
     * Set the colormap to be used for the filter.
     * 
     * @param colormap the colormap
     * @see #getColormap
     */
    public void setColormap(Colormap colormap) {
        this.colormap = colormap;
    }

    /**
     * Get the colormap to be used for the filter.
     * 
     * @return the colormap
     * @see #setColormap
     */
    public Colormap getColormap() {
        return colormap;
    }

    public int filterRGB(int x, int y, int rgb) {
        // int a = rgb & 0xff000000;
        int r = (rgb >> 16) & 0xff;
        int g = (rgb >> 8) & 0xff;
        int b = rgb & 0xff;
        rgb = (r + g + b) / 3;
        return colormap.getColor(rgb / 255.0f);
    }

    public String toString() {
        return "Colors/Lookup...";
    }

    @Override
    public String key() {
        return LookupTransformation.class.getCanonicalName() + "-" + colormap.hashCode();
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/LevelsTransformation.java
public class LevelsTransformation extends WholeImageTransformation {

    private int[][] lut;

    private float lowLevel = 0;

    private float highLevel = 1;

    private float lowOutputLevel = 0;

    private float highOutputLevel = 1;

    public LevelsTransformation() {
    }

    public void setLowLevel(float lowLevel) {
        this.lowLevel = lowLevel;
    }

    public float getLowLevel() {
        return lowLevel;
    }

    public void setHighLevel(float highLevel) {
        this.highLevel = highLevel;
    }

    public float getHighLevel() {
        return highLevel;
    }

    public void setLowOutputLevel(float lowOutputLevel) {
        this.lowOutputLevel = lowOutputLevel;
    }

    public float getLowOutputLevel() {
        return lowOutputLevel;
    }

    public void setHighOutputLevel(float highOutputLevel) {
        this.highOutputLevel = highOutputLevel;
    }

    public float getHighOutputLevel() {
        return highOutputLevel;
    }

    @Override
    protected int[] filterPixels(int width, int height, int[] inPixels, Rect transformedSpace) {
        Histogram histogram = new Histogram(inPixels, width, height, 0, width);

        int i, j;

        if (histogram.getNumSamples() > 0) {
            lut = new int[3][256];

            float low = lowLevel * 255;
            float high = highLevel * 255;
            if (low == high)
                high++;
            for (i = 0; i < 3; i++) {
                for (j = 0; j < 256; j++)
                    lut[i][j] = PixelUtils.clamp((int) (255 * (lowOutputLevel + (highOutputLevel - lowOutputLevel) * (j - low)
                            / (high - low))));
            }
        } else
            lut = null;

        i = 0;
        for (int y = 0; y < height; y++)
            for (int x = 0; x < width; x++) {
                inPixels[i] = filterRGB(x, y, inPixels[i]);
                i++;
            }
        lut = null;

        return inPixels;
    }

    public int filterRGB(int x, int y, int rgb) {
        if (lut != null) {
            int a = rgb & 0xff000000;
            int r = lut[Histogram.RED][ (rgb >> 16) & 0xff];
            int g = lut[Histogram.GREEN][ (rgb >> 8) & 0xff];
            int b = lut[Histogram.BLUE][rgb & 0xff];

            return a | (r << 16) | (g << 8) | b;
        }
        return rgb;
    }

    public String toString() {
        return "Colors/Levels...";
    }

    @Override
    public String key() {
        return LevelsTransformation.class.getCanonicalName() + "-" + lowLevel + "-" + highLevel + "-" + lowOutputLevel + "-"
                + highOutputLevel;
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/PosterizeTransformation.java
public class PosterizeTransformation extends PointTransformation {

    private int numLevels;

    private int[] levels;

    private boolean initialized = false;

    public PosterizeTransformation() {
        setNumLevels(6);
    }

    /**
     * Set the number of levels in the output image.
     * 
     * @param numLevels the number of levels
     * @see #getNumLevels
     */
    public void setNumLevels(int numLevels) {
        this.numLevels = numLevels;
        initialized = false;
    }

    /**
     * Get the number of levels in the output image.
     * 
     * @return the number of levels
     * @see #setNumLevels
     */
    public int getNumLevels() {
        return numLevels;
    }

    /**
     * Initialize the filter.
     */
    protected void initialize() {
        levels = new int[256];
        if (numLevels != 1)
            for (int i = 0; i < 256; i++)
                levels[i] = 255 * (numLevels * i / 256) / (numLevels - 1);
    }

    public int filterRGB(int x, int y, int rgb) {
        if (!initialized) {
            initialized = true;
            initialize();
        }
        int a = rgb & 0xff000000;
        int r = (rgb >> 16) & 0xff;
        int g = (rgb >> 8) & 0xff;
        int b = rgb & 0xff;
        r = levels[r];
        g = levels[g];
        b = levels[b];
        return a | (r << 16) | (g << 8) | b;
    }

    public String toString() {
        return "Colors/Posterize...";
    }

    @Override
    public String key() {
        return PosterizeTransformation.class.getCanonicalName() + "-" + numLevels;
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/DiffusionTransformation.java
public class DiffusionTransformation extends WholeImageTransformation {

    private final static int[] diffusionMatrix = { 0, 0, 0, 0, 0, 7, 3, 5, 1, };

    private int[] matrix;
    private int sum = 3 + 5 + 7 + 1;
    private boolean serpentine = true;
    private boolean colorDither = true;
    private int levels = 6;

    /**
     * Construct a DiffusionFilter.
     */
    public DiffusionTransformation() {
        setMatrix(diffusionMatrix);
    }

    /**
     * Set whether to use a serpentine pattern for return or not. This can
     * reduce 'avalanche' artifacts in the output.
     * 
     * @param serpentine
     *            true to use serpentine pattern
     * @see #getSerpentine
     */
    public void setSerpentine(boolean serpentine) {
        this.serpentine = serpentine;
    }

    /**
     * Return the serpentine setting.
     * 
     * @return the current setting
     * @see #setSerpentine
     */
    public boolean getSerpentine() {
        return serpentine;
    }

    /**
     * Set whether to use a color dither.
     * 
     * @param colorDither
     *            true to use a color dither
     * @see #getColorDither
     */
    public void setColorDither(boolean colorDither) {
        this.colorDither = colorDither;
    }

    /**
     * Get whether to use a color dither.
     * 
     * @return true to use a color dither
     * @see #setColorDither
     */
    public boolean getColorDither() {
        return colorDither;
    }

    /**
     * Set the dither matrix.
     * 
     * @param matrix
     *            the dither matrix
     * @see #getMatrix
     */
    public void setMatrix(int[] matrix) {
        this.matrix = matrix;
        sum = 0;
        for (int i = 0; i < matrix.length; i++)
            sum += matrix[i];
    }

    /**
     * Get the dither matrix.
     * 
     * @return the dither matrix
     * @see #setMatrix
     */
    public int[] getMatrix() {
        return matrix;
    }

    /**
     * Set the number of dither levels.
     * 
     * @param levels
     *            the number of levels
     * @see #getLevels
     */
    public void setLevels(int levels) {
        this.levels = levels;
    }

    /**
     * Get the number of dither levels.
     * 
     * @return the number of levels
     * @see #setLevels
     */
    public int getLevels() {
        return levels;
    }

    @Override
    protected int[] filterPixels(int width, int height, int[] inPixels, Rect transformedSpace) {
        int[] outPixels = new int[width * height];

        int index = 0;
        int[] map = new int[levels];
        for (int i = 0; i < levels; i++) {
            int v = 255 * i / (levels - 1);
            map[i] = v;
        }
        int[] div = new int[256];
        for (int i = 0; i < 256; i++)
            div[i] = levels * i / 256;

        for (int y = 0; y < height; y++) {
            boolean reverse = serpentine && (y & 1) == 1;
            int direction;
            if (reverse) {
                index = y * width + width - 1;
                direction = -1;
            } else {
                index = y * width;
                direction = 1;
            }
            for (int x = 0; x < width; x++) {
                int rgb1 = inPixels[index];

                int r1 = (rgb1 >> 16) & 0xff;
                int g1 = (rgb1 >> 8) & 0xff;
                int b1 = rgb1 & 0xff;

                if (!colorDither)
                    r1 = g1 = b1 = (r1 + g1 + b1) / 3;

                int r2 = map[div[r1]];
                int g2 = map[div[g1]];
                int b2 = map[div[b1]];

                outPixels[index] = (rgb1 & 0xff000000) | (r2 << 16) | (g2 << 8) | b2;

                int er = r1 - r2;
                int eg = g1 - g2;
                int eb = b1 - b2;

                for (int i = -1; i <= 1; i++) {
                    int iy = i + y;
                    if (0 <= iy && iy < height) {
                        for (int j = -1; j <= 1; j++) {
                            int jx = j + x;
                            if (0 <= jx && jx < width) {
                                int w;
                                if (reverse)
                                    w = matrix[(i + 1) * 3 - j + 1];
                                else
                                    w = matrix[(i + 1) * 3 + j + 1];
                                if (w != 0) {
                                    int k = reverse ? index - j : index + j;
                                    rgb1 = inPixels[k];
                                    r1 = (rgb1 >> 16) & 0xff;
                                    g1 = (rgb1 >> 8) & 0xff;
                                    b1 = rgb1 & 0xff;
                                    r1 += er * w / sum;
                                    g1 += eg * w / sum;
                                    b1 += eb * w / sum;
                                    inPixels[k] = (inPixels[k] & 0xff000000) | (PixelUtils.clamp(r1) << 16)
                                            | (PixelUtils.clamp(g1) << 8) | PixelUtils.clamp(b1);
                                }
                            }
                        }
                    }
                }
                index += direction;
            }
        }

        return outPixels;
    }

    public String toString() {
        return "Colors/Diffusion Dither...";
    }

    @Override
    public String key() {
        return DiffusionTransformation.class.getCanonicalName() + "-" + matrix.hashCode() + "-" + sum + "-"
                + serpentine + "-" + colorDither + "-" + levels;
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/GrayscaleTransformation.java
public class GrayscaleTransformation extends PointTransformation {

	public GrayscaleTransformation() {
		canFilterIndexColorModel = true;
	}

	public int filterRGB(int x, int y, int rgb) {
		int a = rgb & 0xff000000;
		int r = (rgb >> 16) & 0xff;
		int g = (rgb >> 8) & 0xff;
		int b = rgb & 0xff;
//		rgb = (r + g + b) / 3;	// simple average
		rgb = (r * 77 + g * 151 + b * 28) >> 8;	// NTSC luma
		return a | (rgb << 16) | (rgb << 8) | rgb;
	}

	public String toString() {
		return "Colors/Grayscale";
	}

    @Override
    public String key() {
        return GrayscaleTransformation.class.getCanonicalName();
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/TritoneTransformation.java
public class TritoneTransformation extends PointTransformation {

    private int shadowColor = 0xff000000;

    private int midColor = 0xff888888;

    private int highColor = 0xffffffff;

    private int[] lut;

//    public BufferedImage filter(BufferedImage src, BufferedImage dst) {
//        lut = new int[256];
//        for (int i = 0; i < 128; i++) {
//            float t = i / 127.0f;
//            lut[i] = ImageMath.mixColors(t, shadowColor, midColor);
//        }
//        for (int i = 128; i < 256; i++) {
//            float t = (i - 127) / 128.0f;
//            lut[i] = ImageMath.mixColors(t, midColor, highColor);
//        }
//        dst = super.filter(src, dst);
//        lut = null;
//        return dst;
//    }

    public int filterRGB(int x, int y, int rgb) {
        return lut[PixelUtils.brightness(rgb)];
    }

    /**
     * Set the shadow color.
     * 
     * @param shadowColor the shadow color
     * @see #getShadowColor
     */
    public void setShadowColor(int shadowColor) {
        this.shadowColor = shadowColor;
    }

    /**
     * Get the shadow color.
     * 
     * @return the shadow color
     * @see #setShadowColor
     */
    public int getShadowColor() {
        return shadowColor;
    }

    /**
     * Set the mid color.
     * 
     * @param midColor the mid color
     * @see #getmidColor
     */
    public void setMidColor(int midColor) {
        this.midColor = midColor;
    }

    /**
     * Get the mid color.
     * 
     * @return the mid color
     * @see #setmidColor
     */
    public int getMidColor() {
        return midColor;
    }

    /**
     * Set the high color.
     * 
     * @param highColor the high color
     * @see #gethighColor
     */
    public void setHighColor(int highColor) {
        this.highColor = highColor;
    }

    /**
     * Get the high color.
     * 
     * @return the high color
     * @see #sethighColor
     */
    public int getHighColor() {
        return highColor;
    }

    public String toString() {
        return "Colors/Tritone...";
    }

    @Override
    public String key() {
        return TritoneTransformation.class.getCanonicalName() + "-" + shadowColor + "-" + midColor + "-" + highColor;
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/MirrorTransformation.java
public class MirrorTransformation implements Transformation {

    private float opacity = 1.0f;

    private float centreY = 0.5f;

    private float distance;

    private float gap;

    public MirrorTransformation() {
    }

    public MirrorTransformation setDistance(float distance) {
        this.distance = distance;
        return this;
    }

    public float getDistance() {
        return distance;
    }

    public MirrorTransformation setGap(float gap) {
        this.gap = gap;
        return this;
    }

    public float getGap() {
        return gap;
    }

    /**
     * Set the opacity of the reflection.
     * 
     * @param opacity the opacity.
     * @see #getOpacity
     */
    public MirrorTransformation setOpacity(float opacity) {
        this.opacity = opacity;
        return this;
    }

    /**
     * Get the opacity of the reflection.
     * 
     * @return the opacity.
     * @see #setOpacity
     */
    public float getOpacity() {
        return opacity;
    }

    public MirrorTransformation setCentreY(float centreY) {
        this.centreY = centreY;
        return this;
    }

    public float getCentreY() {
        return centreY;
    }

    @Override
    public Bitmap transform(Bitmap source) {
        int width = source.getWidth();
        int height = source.getHeight();

        int h = (int) (centreY * height);
        int d = (int) (gap * height);

        Canvas canvas = new Canvas();
        Paint paint = new Paint();

        Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
        canvas.setBitmap(bitmap);

        // Set background color
        canvas.drawColor(Color.BLACK);

        // Draw upper region
        RectF clipSrcF = new RectF(0, 0, width, height);
        RectF clipDestF = new RectF(0, 0, width, h);
        Matrix m = new Matrix();
        m.setRectToRect(clipSrcF, clipDestF, ScaleToFit.CENTER);
        canvas.drawBitmap(source, m, paint);

        // Draw mirror
        clipSrcF = new RectF(0, 0, width, height);
        clipDestF = new RectF(0, h + d, width, height + d);
        m = new Matrix();
        m.setRectToRect(clipSrcF, clipDestF, ScaleToFit.CENTER);
        m.preTranslate(0, height);
        m.preScale(1, -1);
        canvas.drawBitmap(source, m, paint);

        // Done with source image
        source.recycle();

        // Draw gradient over mirror
        RectF clipGradientF = new RectF(0, 0, width, height);
        m.mapRect(clipGradientF);
        paint.setShader(new LinearGradient(0, h, 0, height, Color.argb(50, 0, 0, 0), Color.argb((int) (opacity * 255), 0, 1, 0),
                Shader.TileMode.CLAMP));
        canvas.drawRect(clipGradientF, paint);

        return bitmap;
    }

    public String toString() {
        return "Effects/Mirror...";
    }

    @Override
    public String key() {
        return MirrorTransformation.class.getCanonicalName() + "-" + opacity + "-" + centreY + "-" + distance + "-" + gap;
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/GrayTransformation.java
public class GrayTransformation extends PointTransformation {

	public GrayTransformation() {
		canFilterIndexColorModel = true;
	}

	public int filterRGB(int x, int y, int rgb) {
		int a = rgb & 0xff000000;
		int r = (rgb >> 16) & 0xff;
		int g = (rgb >> 8) & 0xff;
		int b = rgb & 0xff;
		r = (r+255)/2;
		g = (g+255)/2;
		b = (b+255)/2;
		return a | (r << 16) | (g << 8) | b;
	}

	public String toString() {
		return "Colors/Gray Out";
	}

    @Override
    public String key() {
        return GainTransformation.class.getCanonicalName();
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/SolarizeTransformation.java
public class SolarizeTransformation extends TransferTransformation {

    protected float transferFunction(float v) {
        return v > 0.5f ? 2 * (v - 0.5f) : 2 * (0.5f - v);
    }

    public String toString() {
        return "Colors/Solarize";
    }

    @Override
    public String key() {
        return SolarizeTransformation.class.getCanonicalName();
    }
}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/EqualizeTransformation.java
public class EqualizeTransformation extends WholeImageTransformation {

    private int[][] lut;

    public EqualizeTransformation() {
    }

    @Override
    protected int[] filterPixels(int width, int height, int[] inPixels, Rect transformedSpace) {
        Histogram histogram = new Histogram(inPixels, width, height, 0, width);

        int i, j;

        if (histogram.getNumSamples() > 0) {
            float scale = 255.0f / histogram.getNumSamples();
            lut = new int[3][256];
            for (i = 0; i < 3; i++) {
                lut[i][0] = histogram.getFrequency(i, 0);
                for (j = 1; j < 256; j++)
                    lut[i][j] = lut[i][j - 1] + histogram.getFrequency(i, j);
                for (j = 0; j < 256; j++)
                    lut[i][j] = (int) Math.round(lut[i][j] * scale);
            }
        } else
            lut = null;

        i = 0;
        for (int y = 0; y < height; y++)
            for (int x = 0; x < width; x++) {
                inPixels[i] = filterRGB(x, y, inPixels[i]);
                i++;
            }
        lut = null;

        return inPixels;
    }

    private int filterRGB(int x, int y, int rgb) {
        if (lut != null) {
            int a = rgb & 0xff000000;
            int r = lut[Histogram.RED][(rgb >> 16) & 0xff];
            int g = lut[Histogram.GREEN][(rgb >> 8) & 0xff];
            int b = lut[Histogram.BLUE][rgb & 0xff];

            return a | (r << 16) | (g << 8) | b;
        }
        return rgb;
    }

    public String toString() {
        return "Colors/Equalize";
    }

    @Override
    public String key() {
        return EqualizeTransformation.class.getCanonicalName();
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/BlurTransformation.java
public class BlurTransformation extends ConvolveTransformation {
 	
 	/**
     * A 3x3 convolution kernel for a simple blur.
     */
    protected static float[] blurMatrix = {
		1/14f, 2/14f, 1/14f,
		2/14f, 2/14f, 2/14f,
		1/14f, 2/14f, 1/14f
	};

	public BlurTransformation() {
		super(blurMatrix);
	}

	public String toString() {
		return "Blur/Simple Blur";
	}
}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/MapColorsTransformation.java
public class MapColorsTransformation extends PointTransformation {

    private int oldColor;

    private int newColor;

    /**
     * Construct a MapColorsFilter.
     */
    public MapColorsTransformation() {
        this(0xffffffff, 0xff000000);
    }

    /**
     * Construct a MapColorsFilter.
     * 
     * @param oldColor the color to replace
     * @param newColor the color to replace it with
     */
    public MapColorsTransformation(int oldColor, int newColor) {
        canFilterIndexColorModel = true;
        this.oldColor = oldColor;
        this.newColor = newColor;
    }

    public int filterRGB(int x, int y, int rgb) {
        if (rgb == oldColor)
            return newColor;
        return rgb;
    }

    @Override
    public String key() {
        return MapColorsTransformation.class.getCanonicalName() + "-" + oldColor + "-" + newColor;
    }
}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/HSBAdjustTransformation.java
public class HSBAdjustTransformation extends PointTransformation {

    public float hFactor, sFactor, bFactor;
    private float[] hsb = new float[3];
    
    public HSBAdjustTransformation() {
        this(0, 0, 0);
    }

    public HSBAdjustTransformation(float r, float g, float b) {
        hFactor = r;
        sFactor = g;
        bFactor = b;
        canFilterIndexColorModel = true;
    }

    public HSBAdjustTransformation setHFactor( float hFactor ) {
        this.hFactor = hFactor;
        return this;
    }
    
    public float getHFactor() {
        return hFactor;
    }
    
    public HSBAdjustTransformation setSFactor( float sFactor ) {
        this.sFactor = sFactor;
        return this;
    }
    
    public float getSFactor() {
        return sFactor;
    }
    
    public HSBAdjustTransformation setBFactor( float bFactor ) {
        this.bFactor = bFactor;
        return this;
    }
    
    public float getBFactor() {
        return bFactor;
    }
    
    public int filterRGB(int x, int y, int rgb) {
        int a = rgb & 0xff000000;
        int r = (rgb >> 16) & 0xff;
        int g = (rgb >> 8) & 0xff;
        int b = rgb & 0xff;
        Color.RGBtoHSB(r, g, b, hsb);
        hsb[0] += hFactor;
        while (hsb[0] < 0)
            hsb[0] += Math.PI*2;
        hsb[1] += sFactor;
        if (hsb[1] < 0)
            hsb[1] = 0;
        else if (hsb[1] > 1.0)
            hsb[1] = 1.0f;
        hsb[2] += bFactor;
        if (hsb[2] < 0)
            hsb[2] = 0;
        else if (hsb[2] > 1.0)
            hsb[2] = 1.0f;
        rgb = Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]);
        return a | (rgb & 0xffffff);
    }

    public String toString() {
        return "Colors/Adjust HSB...";
    }

    @Override
    public String key() {
        return HSBAdjustTransformation.class.getCanonicalName() + "-" + hFactor + "-" + sFactor + "-" + bFactor;
    }
}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/RescaleTransformation.java
public class RescaleTransformation extends TransferTransformation {

    private float scale = 1.0f;

    public RescaleTransformation() {
    }

    public RescaleTransformation(float scale) {
        this.scale = scale;
    }

    protected float transferFunction(float v) {
        return v * scale;
    }

    /**
     * Specifies the scale factor.
     * 
     * @param scale the scale factor.
     * @min-value 1
     * @max-value 5+
     * @see #getScale
     */
    public void setScale(float scale) {
        this.scale = scale;
        initialized = false;
    }

    /**
     * Returns the scale factor.
     * 
     * @return the scale factor.
     * @see #setScale
     */
    public float getScale() {
        return scale;
    }

    public String toString() {
        return "Colors/Rescale...";
    }

    @Override
    public String key() {
        return RescaleTransformation.class.getCanonicalName() + "-" + scale;
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/KaleidoscopeTransformation.java
public class KaleidoscopeTransformation extends TransformTransformation {

    private float angle = 0;

    private float angle2 = 0;

    private float centreX = 0.5f;

    private float centreY = 0.5f;

    private int sides = 3;

    private float radius = 0;

    private float icentreX;

    private float icentreY;

    /**
     * Construct a KaleidoscopeFilter with no distortion.
     */
    public KaleidoscopeTransformation() {
        setEdgeAction(CLAMP);
    }

    /**
     * Set the number of sides of the kaleidoscope.
     * 
     * @param sides the number of sides
     * @min-value 2
     * @see #getSides
     */
    public KaleidoscopeTransformation setSides(int sides) {
        this.sides = sides;
        return this;
    }

    /**
     * Get the number of sides of the kaleidoscope.
     * 
     * @return the number of sides
     * @see #setSides
     */
    public int getSides() {
        return sides;
    }

    /**
     * Set the angle of the kaleidoscope.
     * 
     * @param angle the angle of the kaleidoscope.
     * @angle
     * @see #getAngle
     */
    public KaleidoscopeTransformation setAngle(float angle) {
        this.angle = angle;
        return this;
    }

    /**
     * Get the angle of the kaleidoscope.
     * 
     * @return the angle of the kaleidoscope.
     * @see #setAngle
     */
    public float getAngle() {
        return angle;
    }

    /**
     * Set the secondary angle of the kaleidoscope.
     * 
     * @param angle2 the angle
     * @angle
     * @see #getAngle2
     */
    public KaleidoscopeTransformation setAngle2(float angle2) {
        this.angle2 = angle2;
        return this;
    }

    /**
     * Get the secondary angle of the kaleidoscope.
     * 
     * @return the angle
     * @see #setAngle2
     */
    public float getAngle2() {
        return angle2;
    }

    /**
     * Set the centre of the effect in the X direction as a proportion of the image size.
     * 
     * @param centreX the center
     * @see #getCentreX
     */
    public KaleidoscopeTransformation setCentreX(float centreX) {
        this.centreX = centreX;
        return this;
    }

    /**
     * Get the centre of the effect in the X direction as a proportion of the image size.
     * 
     * @return the center
     * @see #setCentreX
     */
    public float getCentreX() {
        return centreX;
    }

    /**
     * Set the centre of the effect in the Y direction as a proportion of the image size.
     * 
     * @param centreY the center
     * @see #getCentreY
     */
    public KaleidoscopeTransformation setCentreY(float centreY) {
        this.centreY = centreY;
        return this;
    }

    /**
     * Get the centre of the effect in the Y direction as a proportion of the image size.
     * 
     * @return the center
     * @see #setCentreY
     */
    public float getCentreY() {
        return centreY;
    }

    /**
     * Set the centre of the effect as a proportion of the image size.
     * 
     * @param centre the center
     * @see #getCentre
     */
    public KaleidoscopeTransformation setCentre(PointF centre) {
        this.centreX = centre.x;
        this.centreY = centre.y;
        return this;
    }

    /**
     * Get the centre of the effect as a proportion of the image size.
     * 
     * @return the center
     * @see #setCentre
     */
    public PointF getCentre() {
        return new PointF(centreX, centreY);
    }

    /**
     * Set the radius of the effect.
     * 
     * @param radius the radius
     * @min-value 0
     * @see #getRadius
     */
    public KaleidoscopeTransformation setRadius(float radius) {
        this.radius = radius;
        return this;
    }

    /**
     * Get the radius of the effect.
     * 
     * @return the radius
     * @see #setRadius
     */
    public float getRadius() {
        return radius;
    }

    @Override
    public Bitmap transform(Bitmap source) {
        icentreX = source.getWidth() * centreX;
        icentreY = source.getHeight() * centreY;
        return super.transform(source);
    }

    protected void transformInverse(int x, int y, float[] out) {
        double dx = x - icentreX;
        double dy = y - icentreY;
        double r = Math.sqrt(dx * dx + dy * dy);
        double theta = Math.atan2(dy, dx) - angle - angle2;
        theta = ImageMath.triangle((float) (theta / Math.PI * sides * .5));
        if (radius != 0) {
            double c = Math.cos(theta);
            double radiusc = radius / c;
            r = radiusc * ImageMath.triangle((float) (r / radiusc));
        }
        theta += angle;

        out[0] = (float) (icentreX + r * Math.cos(theta));
        out[1] = (float) (icentreY + r * Math.sin(theta));
    }

    public String toString() {
        return "Distort/Kaleidoscope...";
    }

    @Override
    public String key() {
        return KaleidoscopeTransformation.class.getCanonicalName() + "-" + angle + "-" + angle2 + "-" + centreX + "-" + centreY + "-"
                + sides + "-" + radius + "-" + icentreX + "-" + icentreY;
    }

}
picasso-transformations/src/main/java/com/picassotransformations/jhlabs/EmbossTransformation.java
public class EmbossTransformation extends WholeImageTransformation {

    private final static float pixelScale = 255.9f;

    private float azimuth = 135.0f * ImageMath.PI / 180.0f, elevation = 30.0f * ImageMath.PI / 180f;
    private boolean emboss = false;
    private float width45 = 3.0f;

    public EmbossTransformation() {
    }

    public void setAzimuth(float azimuth) {
        this.azimuth = azimuth;
    }

    public float getAzimuth() {
        return azimuth;
    }

    public void setElevation(float elevation) {
        this.elevation = elevation;
    }

    public float getElevation() {
        return elevation;
    }

    public void setBumpHeight(float bumpHeight) {
        this.width45 = 3 * bumpHeight;
    }

    public float getBumpHeight() {
        return width45 / 3;
    }

    public void setEmboss(boolean emboss) {
        this.emboss = emboss;
    }

    public boolean getEmboss() {
        return emboss;
    }

    @Override
    protected int[] filterPixels(int width, int height, int[] inPixels, Rect transformedSpace) {
        int index = 0;
        int[] outPixels = new int[width * height];

        int[] bumpPixels;
        int bumpMapWidth, bumpMapHeight;

        bumpMapWidth = width;
        bumpMapHeight = height;
        bumpPixels = new int[bumpMapWidth * bumpMapHeight];
        for (int i = 0; i < inPixels.length; i++)
            bumpPixels[i] = PixelUtils.brightness(inPixels[i]);

        int Nx, Ny, Nz, Lx, Ly, Lz, Nz2, NzLz, NdotL;
        int shade, background;

        Lx = (int) (Math.cos(azimuth) * Math.cos(elevation) * pixelScale);
        Ly = (int) (Math.sin(azimuth) * Math.cos(elevation) * pixelScale);
        Lz = (int) (Math.sin(elevation) * pixelScale);

        Nz = (int) (6 * 255 / width45);
        Nz2 = Nz * Nz;
        NzLz = Nz * Lz;

        background = Lz;

        int bumpIndex = 0;

        for (int y = 0; y < height; y++, bumpIndex += bumpMapWidth) {
            int s1 = bumpIndex;
            int s2 = s1 + bumpMapWidth;
            int s3 = s2 + bumpMapWidth;

            for (int x = 0; x < width; x++, s1++, s2++, s3++) {
                if (y != 0 && y < height - 2 && x != 0 && x < width - 2) {
                    Nx = bumpPixels[s1 - 1] + bumpPixels[s2 - 1] + bumpPixels[s3 - 1] - bumpPixels[s1 + 1]
                            - bumpPixels[s2 + 1] - bumpPixels[s3 + 1];
                    Ny = bumpPixels[s3 - 1] + bumpPixels[s3] + bumpPixels[s3 + 1] - bumpPixels[s1 - 1] - bumpPixels[s1]
                            - bumpPixels[s1 + 1];

                    if (Nx == 0 && Ny == 0)
                        shade = background;
                    else if ((NdotL = Nx * Lx + Ny * Ly + NzLz) < 0)
                        shade = 0;
                    else
                        shade = (int) (NdotL / Math.sqrt(Nx * Nx + Ny * Ny + Nz2));
                } else
                    shade = background;

                if (emboss) {
                    int rgb = inPixels[index];
                    int a = rgb & 0xff000000;
                    int r = (rgb >> 16) & 0xff;
                    int g = (rgb >> 8) & 0xff;
                    int b = rgb & 0xff;
                    r = (r * shade) >> 8;
                    g = (g * shade) >> 8;
                    b = (b * shade) >> 8;
                    outPixels[index++] = a | (r << 16) | (g << 8) | b;
                } else
                    outPixels[index++] = 0xff000000 | (shade << 16) | (shade << 8) | shade;
            }
        }

        return outPixels;
    }

    public String toString() {
        return "Stylize/Emboss...";
    }

    @Override
    public String key() {
        return EmbossTransformation.class.getCanonicalName() + "-" + pixelScale + "-" + azimuth + "-" + elevation + "-"
                + emboss + "-" + width45;
    }

}
picasso-transformations/src/main/java/com/picassotransformations/renderscript/RSGaussianBlurTransformation.java
public class RSGaussianBlurTransformation implements Transformation {

    private Context mContext;

    private int mRadius;
    
    /**
     * Constructor.
     * 
     * @param context The context.
     * @param radius The blur radius: (0, 25]
     */
    public RSGaussianBlurTransformation(Context context, int radius) {
        mContext = context;
        mRadius = radius;
    }

    @Override
    public Bitmap transform(Bitmap source) {
        if (Build.VERSION.SDK_INT < 17) {
            return source;
        }
        
        RenderScript rs = RenderScript.create(mContext);
        Allocation input = Allocation.createFromBitmap(rs, source, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
        Allocation output = Allocation.createTyped(rs, input.getType());
        ScriptIntrinsicBlur script = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        script.setRadius(mRadius);
        script.setInput(input);
        script.forEach(output);
        output.copyTo(source);
        return source;
    }

    @Override
    public String key() {
        return RSGaussianBlurTransformation.class.getCanonicalName() + "-" + mRadius;
    }

}


Question: import java.util.Arrays;
import java.util.List;
import android.os.Parcel;
import android.os.Parcelable;
import com.picassotransformations.StackBlurTransformation;
import com.picassotransformations.jhlabs.BlockTransformation;
import com.picassotransformations.jhlabs.BlurTransformation;
import com.picassotransformations.jhlabs.ChannelMixTransformation;
import com.picassotransformations.jhlabs.ContrastTransformation;
import com.picassotransformations.jhlabs.CurvesTransformation;
import com.picassotransformations.jhlabs.DiffusionTransformation;
import com.picassotransformations.jhlabs.DitherTransformation;
import com.picassotransformations.jhlabs.EdgeTransformation;
import com.picassotransformations.jhlabs.EmbossTransformation;
import com.picassotransformations.jhlabs.EqualizeTransformation;
import com.picassotransformations.jhlabs.ExposureTransformation;
import com.picassotransformations.jhlabs.GainTransformation;
import com.picassotransformations.jhlabs.GrayTransformation;
import com.picassotransformations.jhlabs.GrayscaleTransformation;
import com.picassotransformations.jhlabs.HSBAdjustTransformation;
import com.picassotransformations.jhlabs.InvertTransformation;
import com.picassotransformations.jhlabs.KaleidoscopeTransformation;
import com.picassotransformations.jhlabs.LevelsTransformation;
import com.picassotransformations.jhlabs.LookupTransformation;
import com.picassotransformations.jhlabs.MapColorsTransformation;
import com.picassotransformations.jhlabs.MarbleTransformation;
import com.picassotransformations.jhlabs.MaskTransformation;
import com.picassotransformations.jhlabs.MirrorTransformation;
import com.picassotransformations.jhlabs.PolarTransformation;
import com.picassotransformations.jhlabs.PosterizeTransformation;
import com.picassotransformations.jhlabs.QuantizeTransformation;
import com.picassotransformations.jhlabs.RGBAdjustTransformation;
import com.picassotransformations.jhlabs.RescaleTransformation;
import com.picassotransformations.jhlabs.SolarizeTransformation;
import com.picassotransformations.jhlabs.ThresholdTransformation;
import com.picassotransformations.jhlabs.TritoneTransformation;
import com.picassotransformations.renderscript.RSGaussianBlurTransformation;
import com.squareup.picasso.Transformation;
/*
 * Copyright (C) 2014 Tanner Perrien
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.picassotransformations;





public enum TransformationCategory implements Parcelable {

    COLOR_ADJUSTMENT("Color Adjustment",
            Arrays.asList(
                    new ChannelMixTransformation(),
                    new ContrastTransformation().setBrightness(.7f).setContrast(.5f),
                    new CurvesTransformation(),
                    new DiffusionTransformation(),
                    new DitherTransformation(),
                    new ExposureTransformation(),
                    new GainTransformation(),
                    new GrayTransformation(),
                    new GrayscaleTransformation(),
Answer:                     new HSBAdjustTransformation(.5f,.5f,.5f),