by Chiu-Ki Chan
LinearGradient
, RadialGradient
, SweepGradient
, BitmapGradient
, ComposeGradient
LightingColorFilter
, ColorMatrixColorFilter
, PorterDuffColorFilter
BlurMaskFilter
, EmbossMaskFilter
<shape xmlns:android="http://schemas.android.com/apk/res/android" type="rectangle"> <gradient android:startColor="@color/blue" android:centerColor="@color/white" android:endColor="@color/red" android:angle="45"/> </shape>
LinearGradient extends Shader
public class RainbowTextView extends TextView { protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); int[] rainbow = getRainbowColors(); Shader shader = new LinearGradient(0, 0, 0, w, rainbow, null, Shader.TileMode.MIRROR); Matrix matrix = new Matrix(); matrix.setRotate(90); shader.setLocalMatrix(matrix); getPaint().setShader(shader); }
private int[] getRainbowColors() { return new int[] { getResources().getColor(R.color.rainbow_red), getResources().getColor(R.color.rainbow_yellow), getResources().getColor(R.color.rainbow_green), getResources().getColor(R.color.rainbow_blue), getResources().getColor(R.color.rainbow_purple) }; } }
<bitmap xmlns:android="http://schemas.android.com/apk/res/android" android:src="@drawable/cheetah_tile" android:tileMode="repeat"/>
BitmapShader extends Shader
Bitmap bitmap = BitmapFactory.decodeResource( getResources(), R.drawable.cheetah_tile); Shader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); textView.getPaint().setShader(shader);
![]() CLAMP | ![]() REPEAT | ![]() MIRROR |
public class PeekThroughImageView extends ImageView { private final float radius; private Paint paint = null; private float x; private float y; private boolean shouldDrawOpening = false; public boolean onTouchEvent(MotionEvent motionEvent) { int action = motionEvent.getAction(); shouldDrawOpening = (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE); x = motionEvent.getX(); y = motionEvent.getY(); invalidate(); return true; } }
protected void onDraw(Canvas canvas) { if (paint == null) { Bitmap original = Bitmap.createBitmap( getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas originalCanvas = new Canvas(original); super.onDraw(originalCanvas); // ImageView Shader shader = new BitmapShader(original, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); paint = new Paint(); paint.setShader(shader); } canvas.drawColor(Color.GRAY); if (shouldDrawOpening) { canvas.drawCircle(x, y - radius, radius, paint); } }
[ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t ]
Apply to [R, G, B, A], after clamping:
R' = a*R + b*G + c*B + d*A + e; G' = f*R + g*G + h*B + i*A + j; B' = k*R + l*G + m*B + n*A + o; A' = p*R + q*G + r*B + s*A + t;
Identity:
[ R, [ 1, 0, 0, 0, 0, [ R, G, * 0, 1, 0, 0, 0, = G, B, 0, 0, 1, 0, 0, B, A ] 0, 0, 0, 1, 0 ] A ]
Bitmap bitmap = Bitmap.createBitmap(original.getWidth(), original.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(); paint.setColorFilter(new ColorMatrixColorFilter( getColorMatrix())); canvas.drawBitmap(original, 0, 0, paint); return bitmap;
private ColorMatrix getColorMatrix() { ColorMatrix colorMatrix = new ColorMatrix(); colorMatrix.setSaturation(0); return colorMatrix; }
colorMatrix.setSaturation(0)
[ 0.213, 0.715, 0.072, 0, 0, 0.213, 0.715, 0.072, 0, 0, 0.213, 0.715, 0.072, 0, 0, 0, 0, 0, 1, 0 ] // 0.213 + 0.715 + 0.072 = 1
private ColorMatrix getColorMatrix() { ColorMatrix colorMatrix = new ColorMatrix(); colorMatrix.setSaturation(0); ColorMatrix colorScale = new ColorMatrix(); colorScale.setScale(1, 1, 0.8f, 1); // Convert to grayscale, then apply brown color colorMatrix.postConcat(colorScale); return colorMatrix; }
colorMatrix.setScale(1, 1, 0.8f, 1)
[ 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0.8, 0, 0, 0, 0, 0, 1, 0 ]
private ColorMatrix getColorMatrix() { ColorMatrix colorMatrix = new ColorMatrix(); colorMatrix.setSaturation(0); float m = 255f; float t = -255*128f; ColorMatrix threshold = new ColorMatrix(new float[] { m, 0, 0, 1, t, 0, m, 0, 1, t, 0, 0, m, 1, t, 0, 0, 0, 1, 0 }); // Convert to grayscale, then scale and clamp colorMatrix.postConcat(threshold); return colorMatrix; }
private ColorMatrix getColorMatrix() { return new ColorMatrix(new float[] { -1, 0, 0, 0, 255, 0, -1, 0, 0, 255, 0, 0, -1, 0, 255, 0, 0, 0, 1, 0 }); }
private ColorMatrix getColorMatrix() { return new ColorMatrix(new float[] { 0, 0, 0, 0, 0, 0.3f, 0, 0, 0, 50, 0, 0, 0, 0, 255, 0.2f, 0.4f, 0.4f, 0, -30 }); }
private ColorMatrix getColorMatrix() { return new ColorMatrix(new float[] { 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0.2f, 0, 0, 0, 50, 0.2f, 0.2f, 0.2f, 0, -20 }); }
/** Create a colorfilter that multiplies the RGB channels by one color, and then adds a second color. */ LightingColorFilter(int mul, int add)
R' = R * mul.R + add.R G' = G * mul.G + add.G B' = B * mul.B + add.B
Little brother of ColorMatrixColorFilter
[ mul.R, 0, 0, 0, add.R 0, mul.G, 0, 0, add.G, 0, 0, mul.B, 0, add.B, 0, 0, 0, 1, 0 ]
mul.R = Color.red(mul) / 255f
e.g. #ff0000 → 0xff / 255 = 255 / 255 = 1
Four colored quarters
public class FourColorsImageView extends ImageView { private Bitmap bitmap = null; protected void onDraw(Canvas canvas) { if (bitmap == null) { Bitmap quarter = Bitmap.createBitmap( getWidth()/2, getHeight()/2, Bitmap.Config.ARGB_8888); Canvas quarterCanvas = new Canvas(quarter); quarterCanvas.scale(0.5f, 0.5f); super.onDraw(quarterCanvas); quarterCanvas.scale(2, 2); createBitmap(quarter); quarter.recycle(); } canvas.drawBitmap(bitmap, 0, 0, null); } }
private void createBitmap(Bitmap quarter) { bitmap = Bitmap.createBitmap( getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(); // Top left paint.setColorFilter(new LightingColorFilter(Color.RED, 0)); canvas.drawBitmap(quarter, 0, 0, paint); // Top right paint.setColorFilter(new LightingColorFilter(Color.YELLOW, 0)); canvas.drawBitmap(quarter, getWidth()/2, 0, paint); // Bottom left paint.setColorFilter(new LightingColorFilter(Color.BLUE, 0)); canvas.drawBitmap(quarter, 0, getHeight()/2, paint); // Bottom right paint.setColorFilter(new LightingColorFilter(Color.GREEN, 0)); canvas.drawBitmap(quarter, getWidth()/2, getHeight()/2, paint); }
DST
(what was already there)
SRC
(what we are drawing)
Bitmap bitmap = Bitmap.createBitmap( original.getWidth(), original.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); // Draw the original bitmap (DST during Porter-Duff transfer) canvas.drawBitmap(original, 0, 0, null); // DST_IN = Whatever was there, keep the part that overlaps // with what I'm drawing now Paint maskPaint = new Paint(); maskPaint.setXfermode( new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); canvas.drawBitmap(mask, 0, 0, maskPaint);
DST
(what was already there)
SRC
(what we are drawing)
DST
(what was already there)
SRC
(what we are drawing)
Paint overPaint = new Paint(); // DST_OVER = Whatever was there (DST), put it over what // we are drawing now overPaint.setXfermode( new PorterDuffXfermode(PorterDuff.Mode.DST_OVER)); // Draw original (SRC) with dim filter overPaint.setColorFilter(createDimFilter()); canvas.drawBitmap(original, 0, 0, overPaint);
DST
(what was already there)
SRC
(what we are drawing)
private ColorFilter createDimFilter() { ColorMatrix colorMatrix = new ColorMatrix(); colorMatrix.setSaturation(0f); float scale = 0.5f; colorMatrix.setScale(scale, scale, scale, 1f); return new ColorMatrixColorFilter(colorMatrix); }
DST
(what was already there)
SRC
(what we are drawing)
int strokeWidth = getResources().getDimensionPixelSize( R.dimen.dashed_text_stroke_width); final HollowSpan span = new HollowSpan(strokeWidth); String text = textView.getText().toString(); final SpannableString spannableString = new SpannableString(text); spannableString.setSpan(span, 0, text.length(), 0);
private static class HollowSpan extends ReplacementSpan { private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Path path = new Path(); private int width; public HollowSpan(int strokeWidth) { paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(strokeWidth); } public void setPathEffect(PathEffect effect) { paint.setPathEffect(effect); } }
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { this.paint.setColor(paint.getColor()); width = (int) (paint.measureText(text, start, end) + this.paint.getStrokeWidth()); return width; } public void draw( Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { path.reset(); paint.getTextPath(text.toString(), start, end, x, y, path); path.close(); canvas.translate(this.paint.getStrokeWidth() / 2, 0); canvas.drawPath(path, this.paint); canvas.translate(-this.paint.getStrokeWidth() / 2, 0); }
PathEffect dash = new DashPathEffect( new float[] { strokeWidth * 3, strokeWidth }, 0); span.setPathEffect(dash);
private static class HollowSpan extends ReplacementSpan { private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private final Path path = new Path(); private int width; public HollowSpan(int strokeWidth) { paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(strokeWidth); } public void setPathEffect(PathEffect effect) { paint.setPathEffect(effect); } // In draw, canvas.drawPath(path, this.paint); }
PathEffect dash = new DashPathEffect( new float[] { strokeWidth * 3, strokeWidth }, 0); PathEffect corner = new CornerPathEffect(strokeWidth); PathEffect effect = new ComposePathEffect(dash, corner); span.setPathEffect(effect);
private PathEffect getTrianglePathEffect(int strokeWidth) { return new PathDashPathEffect( getTriangle(strokeWidth), strokeWidth, 0.0f, PathDashPathEffect.Style.ROTATE); } private Path getTriangle(float size) { Path path = new Path(); float half = size / 2; path.moveTo(-half, -half); path.lineTo(half, -half); path.lineTo(0, half); path.close(); return path; }
private void applyFilter( TextView textView, float[] direction, float ambient, float specular, float blurRadius) { if (Build.VERSION.SDK_INT >= 11) { textView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); } EmbossMaskFilter filter = new EmbossMaskFilter( direction, ambient, specular, blurRadius); textView.getPaint().setMaskFilter(filter); }
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
(Not supported by hardware acceleration)
applyFilter(emboss, new float[] { 0f, 1f, 0.5f }, 0.8f, 3f, 3f);
applyFilter(deboss, new float[] { 0f, -1f, 0.5f }, 0.8f, 15f, 1f);
private void applyFilter( TextView textView, BlurMaskFilter.Blur style) { if (Build.VERSION.SDK_INT >= 11) { textView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); } textView.setText(style.name()); float radius = textView.getTextSize() / 10; BlurMaskFilter filter = new BlurMaskFilter(radius, style); textView.getPaint().setMaskFilter(filter); }
@TargetApi(17)
private Bitmap blur(Bitmap original, float radius) { Bitmap bitmap = Bitmap.createBitmap( original.getWidth(), original.getHeight(), Bitmap.Config.ARGB_8888); RenderScript rs = RenderScript.create(this); Allocation allocIn = Allocation.createFromBitmap(rs, original); Allocation allocOut = Allocation.createFromBitmap(rs, bitmap); ScriptIntrinsicBlur blur = ScriptIntrinsicBlur.create( rs, Element.U8_4(rs)); blur.setInput(allocIn); blur.setRadius(radius); blur.forEach(allocOut); allocOut.copyTo(bitmap); rs.destroy(); return bitmap; }
private Bitmap convolve(Bitmap original, float[] coefficients) { Bitmap bitmap = Bitmap.createBitmap( original.getWidth(), original.getHeight(), Bitmap.Config.ARGB_8888); RenderScript rs = RenderScript.create(this); Allocation allocIn = Allocation.createFromBitmap(rs, original); Allocation allocOut = Allocation.createFromBitmap(rs, bitmap); ScriptIntrinsicConvolve3x3 convolution = ScriptIntrinsicConvolve3x3.create(rs, Element.U8_4(rs)); convolution.setInput(allocIn); convolution.setCoefficients(coefficients); convolution.forEach(allocOut); allocOut.copyTo(bitmap); // { 1, 1, 1, re.destroy(); // 1, 1, 1, return bitmap; // 1, 1, 1 } / 9 }
private Bitmap convolve(Bitmap original, float[] coefficients) { Bitmap bitmap = Bitmap.createBitmap( original.getWidth(), original.getHeight(), Bitmap.Config.ARGB_8888); RenderScript rs = RenderScript.create(this); Allocation allocIn = Allocation.createFromBitmap(rs, original); Allocation allocOut = Allocation.createFromBitmap(rs, bitmap); ScriptIntrinsicConvolve3x3 convolution = ScriptIntrinsicConvolve3x3.create(rs, Element.U8_4(rs)); convolution.setInput(allocIn); convolution.setCoefficients(coefficients); convolution.forEach(allocOut); allocOut.copyTo(bitmap); // { 0, -1, 0, re.destroy(); // -1 , 5, -1, return bitmap; // 0, -1, 0 } }
private Bitmap convolve(Bitmap original, float[] coefficients) { Bitmap bitmap = Bitmap.createBitmap( original.getWidth(), original.getHeight(), Bitmap.Config.ARGB_8888); RenderScript rs = RenderScript.create(this); Allocation allocIn = Allocation.createFromBitmap(rs, original); Allocation allocOut = Allocation.createFromBitmap(rs, bitmap); ScriptIntrinsicConvolve3x3 convolution = ScriptIntrinsicConvolve3x3.create(rs, Element.U8_4(rs)); convolution.setInput(allocIn); convolution.setCoefficients(coefficients); convolution.forEach(allocOut); allocOut.copyTo(bitmap); // { -1, -1, -1, re.destroy(); // -1 , 8, -1, return bitmap; // -1, -1, -1 } }
private Bitmap convolve(Bitmap original, float[] coefficients) { Bitmap bitmap = Bitmap.createBitmap( original.getWidth(), original.getHeight(), Bitmap.Config.ARGB_8888); RenderScript rs = RenderScript.create(this); Allocation allocIn = Allocation.createFromBitmap(rs, original); Allocation allocOut = Allocation.createFromBitmap(rs, bitmap); ScriptIntrinsicConvolve3x3 convolution = ScriptIntrinsicConvolve3x3.create(rs, Element.U8_4(rs)); convolution.setInput(allocIn); convolution.setCoefficients(coefficients); convolution.forEach(allocOut); allocOut.copyTo(bitmap); // { 0, 20, 0, re.destroy(); // 20, -59, 20, return bitmap; // 1, 13, 0 } / 7 }