I am trying to make ComposeShader using LinearGradients but may have few problem.
1) If I am only use one LinearGradient like
lgA = new LinearGradient(0, 0, 0, h, 0xff000000, 0xffffffff, Shader.TileMode.CLAMP);
then above part of the requirement correct
2) If use ComposeShader then result not like actual requirement
Actual Requirment :
Then how I can solve this problem please any one help me for this problem.
public class DrawGradient extends View {
Paint paint;
LinearGradient lgA;
LinearGradient lgB;
ComposeShader shader;
public DrawGradient(Context context) {
super(context);
initView();
}
private void initView() {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
paint = new Paint();
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
lgA = new LinearGradient(0, 0, w, h, 0xff000000, 0xffffffff, Shader.TileMode.CLAMP);
lgB = new LinearGradient(0, h, w, 0, 0xffffffff, 0xff000000, Shader.TileMode.CLAMP);
shader = new ComposeShader(lgA, lgB, PorterDuff.Mode.MULTIPLY);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setShader(shader);
canvas.drawPaint(paint);
}
}
Code Output :
Since this four months I imagine this will be of little help now, but here it is for anyone else with a similar problem.
The problem lies in these two lines:
lgA = new LinearGradient(0, 0, w, h, 0xff000000, 0xffffffff, Shader.TileMode.CLAMP);
lgB = new LinearGradient(0, h, w, 0, 0xffffffff, 0xff000000, Shader.TileMode.CLAMP);
You've created two linear gradients; one going from top-left to bottom-left, the other going from bottom-left to top-right. What you are looking for is this:
lgA = new LinearGradient(0, 0, 0, h/2, Color.BLACK, Color.WHITE, Shader.TileMode.CLAMP);
lgB = new LinearGradient(0, h/2, 0, h, Color.WHITE, Color.BLACK, Shader.TileMode.CLAMP);
With these changes, it worked for me. It's important to note that ComposeShader wouldn't work without the line:
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
For reasons explained here.
ComposeShader seems to be mostly used for overlaying Shaders, such as LinearGradients, rather than creating a three colour transition which you're looking for. Alternatively you could have used LinearGradient by itself, but with the other constructor, which can take more than two different colours. E.g.
int[] colors = {Color.BLACK,Color.WHITE,Color.BLACK};
float[] pos = {0.0f,0.5f,1.0f};
paint.setShader(new LinearGradient(0,0,0,h,colors,pos,Shader.TileMode.CLAMP));
This would have given you the same effect, without needing ComposeShader. However I was using this method in my own project, but using Color.TRANSPARENT in the gradient and it was coming out black rather than transparent.
Related
I want to implement color palette into my application. This is the main onDraw method.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int h = canvas.getHeight();
int w = canvas.getWidth();
if (paint == null) {
paint = new Paint();
luar = new LinearGradient(0.f, 0.f, 0.f, h, 0xffffffff, 0xff000000, TileMode.CLAMP);
}
int rgb = Color.HSVToColor(color);
Shader dalam = new LinearGradient(0.f, 0.f, w, 0.f, 0xffffffff, rgb, TileMode.CLAMP);
ComposeShader shader = new ComposeShader( luar, dalam, PorterDuff.Mode.MULTIPLY);
paint.setShader(shader);
canvas.drawRect(0.f, 0.f, w, h, paint);
}
But, ComposeShader shader seems not working.
That is,
ComposeShader shader = new ComposeShader( dalam, luar, PorterDuff.Mode.MULTIPLY);
shows only vertical gradient. And if I change the parameters:
ComposeShader shader = new ComposeShader( luar, dalam, PorterDuff.Mode.MULTIPLY);
it shows horizontal gradient. No multiplication! Why?
I try to hide a picture and show interactively its portions when user touches a screen. I tried numerous approaches like having background view that is overlapsed with views that I will make transparent which somehow worked. A final solution shall be a single custom view which will give me more painting flexibility.
Activity:
hiddenPicture.setBackgroundResource(R.drawable.picture);
View:
init()
eraserPaint = new Paint();
eraserPaint.setColor(Color.TRANSPARENT);
// eraserPaint.setAlpha(0);
eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
veilPaint = new Paint();
veilPaint.setColor(Color.GRAY);
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0, 0, w, h, veilPaint);
canvas.drawOval(new RectF(20, 20, 220, 220), eraserPaint);
}
The problem is that the oval is black. I found tons of similar questions but I am too junior in Android to apply them to my case. What shall I do to erase oval in grey veil and show background picture? Thank you.
Update:
I found nice blog: http://www.41post.com/4794/programming/android-rendering-a-path-with-a-bitmap-fill
I recycled it to my code:
fillBMP = BitmapFactory.decodeResource(context.getResources(), R.drawable.picture);
fillBmpShader = new BitmapShader(fillBMP, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
eraserPaint.setColor(0xFFFFFFFF);
eraserPaint.setStyle(Paint.Style.FILL);
eraserPaint.setShader(fillBmpShader);
It seemed to work but the problem is that it does not scale the image used as brush. Is really the only solution to implement onDraw() this way:
Paint the picture
Paint the veil except uncovered parts
I worry about the performance. I do not want to paint complete screen after each user interaction. I would prefer to repaint only relevant parts. Is this even possible or am I over-optimizing already?
I have to maintain and paint two bitmaps - one for background and second for veil with transparent hollows:
private void setupDrawing() {
eraserPaint = new Paint();
eraserPaint.setColor(Color.TRANSPARENT);
eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
veilPaint = new Paint();
veilPaint.setColor(Color.GREEN);
canvasPaint = new Paint(Paint.DITHER_FLAG);
}
// http://developer.android.com/training/custom-views/custom-drawing.html
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
drawCanvas.drawRect(0, 0, w, h, veilPaint);
}
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(fillBitmap, 0, 0, canvasPaint);
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
}
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
drawCanvas.drawCircle(event.getX(), event.getY(), 150, eraserPaint);
invalidate();
}
return true;
}
I'm developing an android app, I need to add a watermark effect (of an image) to a EditText of several lines. What is the best approach to achieve this?
Here is a great tutorial on android image processing. Below is the source code from the site
public static Bitmap mark(Bitmap src, String watermark, Point location, Color color, int alpha, int size, boolean underline) {
int w = src.getWidth();
int h = src.getHeight();
Bitmap result = Bitmap.createBitmap(w, h, src.getConfig());
Canvas canvas = new Canvas(result);
canvas.drawBitmap(src, 0, 0, null);
Paint paint = new Paint();
paint.setColor(color);
paint.setAlpha(alpha);
paint.setTextSize(size);
paint.setAntiAlias(true);
paint.setUnderlineText(underline);
canvas.drawText(watermark, location.x, location.y, paint);
return result;
}
I'm trying to mask Bitmap with gradient alpha at bottom. Gradient size are fixed and independed of Bitmap size. But it draws incorrect: bottom of gradient at top, than top.
What's wrong?
There is sample code:
final int GRADIENT_HEIGHT = 32;
public Bitmap addGradient(Bitmap src) {
int w = src.getWidth();
int h = src.getHeight();
Bitmap overlay = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(overlay);
canvas.drawBitmap(src, 0, 0, null);
Paint paint = new Paint();
LinearGradient shader = new LinearGradient(0, 0, 0, GRADIENT_HEIGHT, 0xFFFFFFFF, 0x00FFFFFF, TileMode.REPEAT);
paint.setShader(shader);
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
canvas.drawRect(0, h - GRADIENT_HEIGHT, w, h, paint);
return overlay;
}
Thanks!
Change your LinearGradient to this:
LinearGradient shader = new LinearGradient(0, h - GRADIENT_HEIGHT, 0, h, 0xFFFFFFFF, 0x00FFFFFF, Shader.TileMode.CLAMP);
I want to add watermark to images just like flipboard does.
As you can see text is added at the bottom of the images with black transparent background. I want to do the exact same thing. Till now I've managed to write text on image but I am not able to make it's background black transparent just like the above picture.
Here's my code so far which I found from here.
public Bitmap mark(Bitmap src, String watermark) {
int w = src.getWidth();
int h = src.getHeight();
Shader shader = new LinearGradient(0, 0, 100, 0, Color.TRANSPARENT, Color.BLACK, TileMode.CLAMP);
Bitmap result = Bitmap.createBitmap(w, h, src.getConfig());
Canvas canvas = new Canvas(result);
canvas.drawBitmap(src, 0, 0, null);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextSize(50);
paint.setAntiAlias(true);
paint.setShader(shader);
paint.setUnderlineText(false);
canvas.drawText(watermark, 10 , h-15, paint);
return result;
}
I think it doesn't require gradient, you can draw it using simple color and using drawRect() method.
Sample code is below, i gonna take the black background size as 25% of whole image.
public Bitmap mark(Bitmap src, String watermark) {
int w = src.getWidth();
int h = src.getHeight();
Paint bgPaint=new Paint();
bgPaint.setColor(Color.parse("AA000000")); //transparent black,change opacity by changing hex value "AA" between "00" and "FF"
Bitmap result = Bitmap.createBitmap(w, h, src.getConfig());
Canvas canvas = new Canvas(result);
canvas.drawBitmap(src, 0, 0, null);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextSize(50);
paint.setAntiAlias(true);
paint.setUnderlineText(false);
//should draw background first,order is important
int left=0;
int right=w;
int bottom=h;
int top=bottom-(h*.25);
canvas.drawRect(left,top,right,bottom,bgPaint);
canvas.drawText(watermark, 10 , h-15, paint);
return result;
}